diff --git a/DEPS b/DEPS
index 34bc573f..52faa5c 100644
--- a/DEPS
+++ b/DEPS
@@ -133,11 +133,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '69600007e278d1bd3fed1df28a6dd9cb9ed23f20',
+  'skia_revision': 'dca4c43291ced6e02d24ee7f5c5dcbf12ada36ba',
   # 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': '1eab45067108b961de4117fde9584b3099e428e3',
+  'v8_revision': 'f413181b62ee4ad78b9ef3d58c4f395794e16bf1',
   # 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.
@@ -145,11 +145,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'de52ca373d98db8b1de1a5a049cf960b78610052',
+  'angle_revision': '38e282570676f3e434482f8a02ea539b286fd1fa',
   # 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': 'f3b57974620dc86f8040123b55c9c9788c79b0e0',
+  'swiftshader_revision': '4af8826ee4231eba1716f9f32716f0cfbdb893ce',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -164,7 +164,7 @@
   #
   # Note this revision should be updated with
   # third_party/boringssl/roll_boringssl.py, not roll-dep.
-  'boringssl_revision': '3390fd88d716ea599d659c2b72b9a6cd4bb36442',
+  'boringssl_revision': 'aadcce380fe9e5e17ff38f8471e956463fc4df21',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -196,7 +196,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '0a33de82760c4a3f8cb7089d5817cdea2b674291',
+  'catapult_revision': '4e9bccd7a0cf184bba38950b2d9c499366b7488b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -252,7 +252,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_headers_revision': '3beb2a0373101562e1a1206edc33cf64fae87b54',
+  'spv_headers_revision': '111a25e4ae45e2b4d7c18415e1d6884712b958c4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -264,28 +264,29 @@
   # 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': '931311700c7c4b76a15c23eee8792dda125fb97e',
+  'dawn_revision': '991ab98f113f5f51ec87d767b45523b19cd15b9e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '348de99ee1f13e6747da3695387a2c78610f5f62',
+  'quiche_revision': 'f08778a58e717a38fcf59eb1a2ca01b8bf836ad2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
   'ios_webkit_revision': '59e9de61b7b36507836fa8b098e8839d7d995b13',
 
-  #
-  # TODO(crbug.com/941824): These revisions need to be kept in sync
+  # TODO(crbug.com/941824): The values below need to be kept in sync
   # between //DEPS and //buildtools/DEPS, so if you're updating one,
   # update the other. There is a presubmit check that checks that
   # you've done so; if you are adding new tools to //buildtools and
   # hence new revisions to this list, make sure you update the
   # _CheckBuildtoolsRevsAreInSync in PRESUBMIT.py to include the additional
   # revisions.
-  #
+
+  # GN CIPD package version.
+  'gn_version': 'git_revision:0790d3043387c762a6bacb1ae0a9ebe883188ab2',
+
   # Also, if you change these, make sure you update the svn_revisions in
   # //buildtools/deps_revisions.gni.
-  #
   'clang_format_revision': '96636aa0e9f047f17447f2d45a094d0b59ed7917',
   'libcxx_revision': 'a50f5035629b7621e92acef968403f71b7d48553',
   'libcxxabi_revision': '0d529660e32d77d9111912d73f2c74fc5fa2a858',
@@ -313,6 +314,26 @@
   'src/buildtools/clang_format/script':
     Var('chromium_git') + '/chromium/llvm-project/cfe/tools/clang-format.git@' +
     Var('clang_format_revision'),
+  'src/buildtools/linux64': {
+    'packages': [
+      {
+        'package': 'gn/gn/linux-amd64',
+        'version': Var('gn_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'checkout_linux',
+  },
+  'src/buildtools/mac': {
+    'packages': [
+      {
+        'package': 'gn/gn/mac-amd64',
+        'version': Var('gn_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'checkout_mac',
+  },
   'src/buildtools/third_party/libc++/trunk':
     Var('chromium_git') + '/chromium/llvm-project/libcxx.git' + '@' +
     Var('libcxx_revision'),
@@ -322,6 +343,16 @@
   'src/buildtools/third_party/libunwind/trunk':
     Var('chromium_git') + '/external/llvm.org/libunwind.git' + '@' +
     Var('libunwind_revision'),
+  'src/buildtools/win': {
+    'packages': [
+      {
+        'package': 'gn/gn/windows-amd64',
+        'version': Var('gn_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'checkout_win',
+  },
 
   'src/chrome/browser/resources/media_router/extension/src':
     Var('chromium_git') + '/media_router.git' + '@' + '29324b698ccd8920bc81c71d42dadc6310f0ad0f',
@@ -759,9 +790,21 @@
   'src/third_party/ced/src':
     Var('chromium_git') + '/external/github.com/google/compact_enc_det.git' + '@' + 'ba412eaaacd3186085babcd901679a48863c7dd5',
 
+  'src/third_party/checkstyle': {
+      'packages': [
+          {
+              'package': 'chromium/third_party/checkstyle',
+              'version': 'y17J5dqst1qkBcbJyie8jltB2oFOgaQjFZ5k9UpbbbwC',
+          },
+      ],
+      # Must also be downloaded on linux for use on chromium_presubmit.
+      'condition': 'checkout_android or checkout_linux',
+      'dep_type': 'cipd',
+  },
+
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '35b4e4c679389f7ea0852b16a97530b93eea852e',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'f9f298eb11ced7eb906610b37938c2de1d5c7515',
       'condition': 'checkout_linux',
   },
 
@@ -786,7 +829,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e5c289fde0a115ef91448242729d61b4f7174517',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'efe902b20b6ae0d367b354bdaa2e10c19349f880',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -855,7 +898,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'f925eefb2147fded88099fe7943b80e3b9d17cc4',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'b184e41a063792881df531eebbaca591605bacde',
 
   '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'),
@@ -1299,7 +1342,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a0f51b2e123f39c9ff12e621b0b47dd28dd64424',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '35816cc9a150ad6a350bc18e8fe759f501fa3f73',
+    Var('webrtc_git') + '/src.git' + '@' + '69008a87185992aa348ece3f893f6a84f8786ae8',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1340,7 +1383,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@fa2d5d1b4ecfb9c097ee6dd91654fd8317084cca',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@00563b3061f569dd3d81d8ce69c61d3a9b6c44ff',
     'condition': 'checkout_src_internal',
   },
 
@@ -2328,43 +2371,6 @@
                '-s', 'src/third_party/skia',
                '--header', 'src/skia/ext/skia_commit_hash.h'],
   },
-  # Pull GN binaries. This needs to be before running GYP below.
-  {
-    'name': 'gn_win',
-    'pattern': '.',
-    'condition': 'host_os == "win"',
-    'action': [ 'python',
-                'src/third_party/depot_tools/download_from_google_storage.py',
-                '--no_resume',
-                '--no_auth',
-                '--bucket', 'chromium-gn',
-                '-s', 'src/buildtools/win/gn.exe.sha1',
-    ],
-  },
-  {
-    'name': 'gn_mac',
-    'pattern': '.',
-    'condition': 'host_os == "mac"',
-    'action': [ 'python',
-                'src/third_party/depot_tools/download_from_google_storage.py',
-                '--no_resume',
-                '--no_auth',
-                '--bucket', 'chromium-gn',
-                '-s', 'src/buildtools/mac/gn.sha1',
-    ],
-  },
-  {
-    'name': 'gn_linux64',
-    'pattern': '.',
-    'condition': 'host_os == "linux"',
-    'action': [ 'python',
-                'src/third_party/depot_tools/download_from_google_storage.py',
-                '--no_resume',
-                '--no_auth',
-                '--bucket', 'chromium-gn',
-                '-s', 'src/buildtools/linux64/gn.sha1',
-    ],
-  },
   # Pull clang-format binaries using checked-in hashes.
   {
     'name': 'clang_format_win',
@@ -2652,20 +2658,6 @@
     'condition': 'checkout_android or checkout_linux',
     'action': ['vpython', 'src/chrome/android/profiles/update_afdo_profile.py'],
   },
-  # Download checkstyle for use in PRESUBMIT for Java changes.
-  {
-    'name': 'checkstyle',
-    'pattern': '.',
-    # Must also be downloaded on linux for use on chromium_presubmit.
-    'condition': 'checkout_android or checkout_linux',
-    'action': [ 'python',
-                'src/third_party/depot_tools/download_from_google_storage.py',
-                '--no_resume',
-                '--no_auth',
-                '--bucket', 'chromium-android-tools/checkstyle',
-                '-s', 'src/third_party/checkstyle/checkstyle-8.0-all.jar.sha1'
-    ],
-  },
   {
     'name': 'gvr_static_shim_android_arm_1',
     'pattern': '\\.sha1',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 12de957..c950c89 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -536,9 +536,9 @@
       (),
     ),
     (
-      'sqlite3_initialize',
+      'sqlite3_initialize(',
       (
-        'Instead of sqlite3_initialize, depend on //sql, ',
+        'Instead of calling sqlite3_initialize(), depend on //sql, ',
         '#include "sql/initialize.h" and use sql::EnsureSqliteInitialized().',
       ),
       True,
@@ -3037,7 +3037,7 @@
 
   # Update this regexp if new revisions are added to the files.
   rev_regexp = input_api.re.compile(
-      "'(clang_format|libcxx|libcxxabi|libunwind)_revision':")
+      "'((clang_format|libcxx|libcxxabi|libunwind)_revision|gn_version)':")
 
   # If a user is changing one revision, they need to change the same
   # line in both files. This means that any given change should contain
diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
index d8547ede..34a29e85 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
@@ -126,7 +126,7 @@
                 boolean multiProcess =
                         CommandLine.getInstance().hasSwitch(AwSwitches.WEBVIEW_SANDBOXED_RENDERER);
                 if (multiProcess) {
-                    ChildProcessLauncherHelper.warmUp(appContext);
+                    ChildProcessLauncherHelper.warmUp(appContext, true);
                 }
                 // The policies are used by browser startup, so we need to register the policy
                 // providers before starting the browser process. This only registers java objects
diff --git a/ash/app_list/demo/app_list_demo_views.cc b/ash/app_list/demo/app_list_demo_views.cc
index c7a1784..5a1c172 100644
--- a/ash/app_list/demo/app_list_demo_views.cc
+++ b/ash/app_list/demo/app_list_demo_views.cc
@@ -90,6 +90,7 @@
 
 int main(int argc, const char** argv) {
   ui::ViewsContentClient views_content_client(argc, argv);
-  views_content_client.set_task(base::Bind(&ShowAppList));
+  views_content_client.set_on_pre_main_message_loop_run_callback(
+      base::BindOnce(&ShowAppList));
   return views_content_client.RunMain();
 }
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h
index 4da6b1d1f..8af06e0 100644
--- a/ash/app_list/views/app_list_view.h
+++ b/ash/app_list/views/app_list_view.h
@@ -15,6 +15,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "build/build_config.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
diff --git a/ash/app_list/views/assistant/dialog_plate.cc b/ash/app_list/views/assistant/dialog_plate.cc
index bd3f651e..b8589db9 100644
--- a/ash/app_list/views/assistant/dialog_plate.cc
+++ b/ash/app_list/views/assistant/dialog_plate.cc
@@ -9,6 +9,7 @@
 #include "ash/assistant/ui/assistant_view_delegate.h"
 #include "ash/assistant/ui/base/assistant_button.h"
 #include "ash/assistant/ui/dialog_plate/dialog_plate.h"
+#include "ash/assistant/ui/dialog_plate/mic_view.h"
 #include "ash/assistant/ui/logo_view/base_logo_view.h"
 #include "ash/assistant/util/animation_util.h"
 #include "ash/resources/vector_icons/vector_icons.h"
@@ -351,7 +352,7 @@
   layout_manager->SetFlexForView(spacer, 1);
 
   // Animated voice input toggle.
-  animated_voice_input_toggle_ = new ash::ActionView(
+  animated_voice_input_toggle_ = new ash::MicView(
       this, delegate_, ash::AssistantButtonId::kVoiceInputToggle);
   animated_voice_input_toggle_->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_ASH_ASSISTANT_DIALOG_PLATE_MIC_ACCNAME));
diff --git a/ash/app_list/views/assistant/dialog_plate.h b/ash/app_list/views/assistant/dialog_plate.h
index cb47446..57d9291 100644
--- a/ash/app_list/views/assistant/dialog_plate.h
+++ b/ash/app_list/views/assistant/dialog_plate.h
@@ -11,7 +11,6 @@
 #include "ash/app_list/app_list_export.h"
 #include "ash/assistant/model/assistant_interaction_model_observer.h"
 #include "ash/assistant/model/assistant_query_history.h"
-#include "ash/assistant/ui/dialog_plate/action_view.h"
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "ui/views/controls/button/button.h"
@@ -19,10 +18,10 @@
 #include "ui/views/view.h"
 
 namespace ash {
-class ActionView;
 enum class AssistantButtonId;
 class AssistantViewDelegate;
 class BaseLogoView;
+class MicView;
 }  // namespace ash
 
 namespace ui {
@@ -39,9 +38,9 @@
 
 // DialogPlate is the child of AssistantMainView concerned with providing the
 // means by which a user converses with Assistant. To this end, DialogPlate
-// provides a textfield for use with the keyboard input modality, an
-// ActionView which serves to either commit a text query, or toggle voice
-// interaction as appropriate for the user's current input modality.
+// provides a textfield for use with the keyboard input modality, and a MicView
+// which serves to toggle voice interaction as appropriate for use with the
+// voice input modality.
 class APP_LIST_EXPORT DialogPlate
     : public views::View,
       public views::TextfieldController,
@@ -92,7 +91,7 @@
   views::View* voice_layout_container_;           // Owned by view hierarchy.
   views::ImageButton* keyboard_input_toggle_;     // Owned by view hierarchy.
   views::ImageButton* voice_input_toggle_;        // Owned by view hierarchy.
-  ash::ActionView* animated_voice_input_toggle_;  // Owned by view hierarchy.
+  ash::MicView* animated_voice_input_toggle_;     // Owned by view hierarchy.
   views::Textfield* textfield_;                   // Owned by view hierarchy.
 
   std::unique_ptr<ui::CallbackLayerAnimationObserver> animation_observer_;
diff --git a/ash/assistant/assistant_controller.cc b/ash/assistant/assistant_controller.cc
index 90710374..152577c 100644
--- a/ash/assistant/assistant_controller.cc
+++ b/ash/assistant/assistant_controller.cc
@@ -162,6 +162,8 @@
       Shell::Get()->new_window_controller()->ShowTaskManager();
       break;
     case DeepLinkType::kUnsupported:
+    case DeepLinkType::kLists:
+    case DeepLinkType::kNotes:
     case DeepLinkType::kOnboarding:
     case DeepLinkType::kQuery:
     case DeepLinkType::kReminders:
diff --git a/ash/assistant/ui/BUILD.gn b/ash/assistant/ui/BUILD.gn
index fbc43e2..883d8d4 100644
--- a/ash/assistant/ui/BUILD.gn
+++ b/ash/assistant/ui/BUILD.gn
@@ -58,10 +58,10 @@
     "base/assistant_scroll_view.h",
     "caption_bar.cc",
     "caption_bar.h",
-    "dialog_plate/action_view.cc",
-    "dialog_plate/action_view.h",
     "dialog_plate/dialog_plate.cc",
     "dialog_plate/dialog_plate.h",
+    "dialog_plate/mic_view.cc",
+    "dialog_plate/mic_view.h",
     "logo_view/base_logo_view.cc",
     "logo_view/base_logo_view.h",
     "main_stage/assistant_card_element_view.cc",
@@ -111,8 +111,8 @@
 
   if (enable_cros_libassistant) {
     sources += [
-      "logo_view/logo_view.cc",
-      "logo_view/logo_view.h",
+      "logo_view/logo_view_impl.cc",
+      "logo_view/logo_view_impl.h",
       "logo_view/shape/mic_part_shape.cc",
       "logo_view/shape/mic_part_shape.h",
       "logo_view/shape/shape.cc",
diff --git a/ash/assistant/ui/dialog_plate/dialog_plate.cc b/ash/assistant/ui/dialog_plate/dialog_plate.cc
index 92c9eed6..734342d9 100644
--- a/ash/assistant/ui/dialog_plate/dialog_plate.cc
+++ b/ash/assistant/ui/dialog_plate/dialog_plate.cc
@@ -8,6 +8,7 @@
 #include "ash/assistant/ui/assistant_ui_constants.h"
 #include "ash/assistant/ui/assistant_view_delegate.h"
 #include "ash/assistant/ui/base/assistant_button.h"
+#include "ash/assistant/ui/dialog_plate/mic_view.h"
 #include "ash/assistant/util/animation_util.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -364,7 +365,7 @@
 
   // Animated voice input toggle.
   animated_voice_input_toggle_ =
-      new ActionView(this, delegate_, AssistantButtonId::kVoiceInputToggle);
+      new MicView(this, delegate_, AssistantButtonId::kVoiceInputToggle);
   animated_voice_input_toggle_->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_ASH_ASSISTANT_DIALOG_PLATE_MIC_ACCNAME));
   voice_layout_container_->AddChildView(animated_voice_input_toggle_);
diff --git a/ash/assistant/ui/dialog_plate/dialog_plate.h b/ash/assistant/ui/dialog_plate/dialog_plate.h
index 4fe9881..ee48f99 100644
--- a/ash/assistant/ui/dialog_plate/dialog_plate.h
+++ b/ash/assistant/ui/dialog_plate/dialog_plate.h
@@ -11,7 +11,6 @@
 #include "ash/assistant/model/assistant_interaction_model_observer.h"
 #include "ash/assistant/model/assistant_query_history.h"
 #include "ash/assistant/model/assistant_ui_model_observer.h"
-#include "ash/assistant/ui/dialog_plate/action_view.h"
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "ui/views/controls/button/button.h"
@@ -28,17 +27,17 @@
 
 namespace ash {
 
-class ActionView;
 enum class AssistantButtonId;
 class AssistantViewDelegate;
+class MicView;
 
 // DialogPlate -----------------------------------------------------------------
 
 // DialogPlate is the child of AssistantMainView concerned with providing the
 // means by which a user converses with Assistant. To this end, DialogPlate
-// provides a textfield for use with the keyboard input modality, and an
-// ActionView which serves to either commit a text query, or toggle voice
-// interaction as appropriate for the user's current input modality.
+// provides a textfield for use with the keyboard input modality, and a MicView
+// which serves to toggle voice interaction as appropriate for use with the
+// voice input modality.
 class COMPONENT_EXPORT(ASSISTANT_UI) DialogPlate
     : public views::View,
       public views::TextfieldController,
@@ -95,7 +94,7 @@
   views::View* voice_layout_container_;              // Owned by view hierarchy.
   views::ImageButton* keyboard_input_toggle_;        // Owned by view hierarchy.
   views::ImageButton* voice_input_toggle_;           // Owned by view hierarchy.
-  ActionView* animated_voice_input_toggle_;          // Owned by view hierarchy.
+  MicView* animated_voice_input_toggle_;             // Owned by view hierarchy.
   views::ImageButton* settings_button_;              // Owned by view hierarchy.
   views::Textfield* textfield_;                      // Owned by view hierarchy.
 
diff --git a/ash/assistant/ui/dialog_plate/action_view.cc b/ash/assistant/ui/dialog_plate/mic_view.cc
similarity index 84%
rename from ash/assistant/ui/dialog_plate/action_view.cc
rename to ash/assistant/ui/dialog_plate/mic_view.cc
index b2310314..46dd2fe 100644
--- a/ash/assistant/ui/dialog_plate/action_view.cc
+++ b/ash/assistant/ui/dialog_plate/mic_view.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/assistant/ui/dialog_plate/action_view.h"
+#include "ash/assistant/ui/dialog_plate/mic_view.h"
 
 #include <memory>
 
@@ -26,9 +26,9 @@
 
 }  // namespace
 
-ActionView::ActionView(views::ButtonListener* listener,
-                       AssistantViewDelegate* delegate,
-                       AssistantButtonId button_id)
+MicView::MicView(views::ButtonListener* listener,
+                 AssistantViewDelegate* delegate,
+                 AssistantButtonId button_id)
     : AssistantButton(listener, button_id), delegate_(delegate) {
   InitLayout();
 
@@ -36,23 +36,23 @@
   delegate_->AddInteractionModelObserver(this);
 }
 
-ActionView::~ActionView() {
+MicView::~MicView() {
   delegate_->RemoveInteractionModelObserver(this);
 }
 
-const char* ActionView::GetClassName() const {
-  return "ActionView";
+const char* MicView::GetClassName() const {
+  return "MicView";
 }
 
-gfx::Size ActionView::CalculatePreferredSize() const {
+gfx::Size MicView::CalculatePreferredSize() const {
   return gfx::Size(kPreferredSizeDip, GetHeightForWidth(kPreferredSizeDip));
 }
 
-int ActionView::GetHeightForWidth(int width) const {
+int MicView::GetHeightForWidth(int width) const {
   return kPreferredSizeDip;
 }
 
-void ActionView::InitLayout() {
+void MicView::InitLayout() {
   SetLayoutManager(std::make_unique<views::FillLayout>());
 
   // Voice action container.
@@ -79,12 +79,12 @@
   UpdateState(/*animate=*/false);
 }
 
-void ActionView::OnMicStateChanged(MicState mic_state) {
+void MicView::OnMicStateChanged(MicState mic_state) {
   is_user_speaking_ = false;
   UpdateState(/*animate=*/true);
 }
 
-void ActionView::OnSpeechLevelChanged(float speech_level_db) {
+void MicView::OnSpeechLevelChanged(float speech_level_db) {
   // TODO: Work with UX to determine the threshold.
   constexpr float kSpeechLevelThreshold = -60.0f;
   if (speech_level_db < kSpeechLevelThreshold)
@@ -97,7 +97,7 @@
   }
 }
 
-void ActionView::UpdateState(bool animate) {
+void MicView::UpdateState(bool animate) {
   const AssistantInteractionModel* interaction_model =
       delegate_->GetInteractionModel();
 
diff --git a/ash/assistant/ui/dialog_plate/action_view.h b/ash/assistant/ui/dialog_plate/mic_view.h
similarity index 75%
rename from ash/assistant/ui/dialog_plate/action_view.h
rename to ash/assistant/ui/dialog_plate/mic_view.h
index 3275828..2ac945d3 100644
--- a/ash/assistant/ui/dialog_plate/action_view.h
+++ b/ash/assistant/ui/dialog_plate/mic_view.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_ASSISTANT_UI_DIALOG_PLATE_ACTION_VIEW_H_
-#define ASH_ASSISTANT_UI_DIALOG_PLATE_ACTION_VIEW_H_
+#ifndef ASH_ASSISTANT_UI_DIALOG_PLATE_MIC_VIEW_H_
+#define ASH_ASSISTANT_UI_DIALOG_PLATE_MIC_VIEW_H_
 
 #include "ash/assistant/model/assistant_interaction_model_observer.h"
 #include "ash/assistant/ui/base/assistant_button.h"
@@ -12,21 +12,20 @@
 
 namespace ash {
 
-class ActionView;
 enum class AssistantButtonId;
 class AssistantViewDelegate;
 class BaseLogoView;
 
 // A stateful view belonging to DialogPlate which indicates current user input
 // modality and delivers notification of press events.
-class COMPONENT_EXPORT(ASSISTANT_UI) ActionView
+class COMPONENT_EXPORT(ASSISTANT_UI) MicView
     : public AssistantButton,
       public AssistantInteractionModelObserver {
  public:
-  ActionView(views::ButtonListener* listener,
-             AssistantViewDelegate* delegate,
-             AssistantButtonId button_id);
-  ~ActionView() override;
+  MicView(views::ButtonListener* listener,
+          AssistantViewDelegate* delegate,
+          AssistantButtonId button_id);
+  ~MicView() override;
 
   // AssistantButton:
   const char* GetClassName() const override;
@@ -48,15 +47,15 @@
 
   AssistantViewDelegate* const delegate_;
 
-  BaseLogoView* voice_action_view_;         // Owned by view hierarchy.
+  BaseLogoView* voice_action_view_;  // Owned by view hierarchy.
 
   // True when speech level goes above a threshold and sets LogoView in
   // kUserSpeaks state.
   bool is_user_speaking_ = false;
 
-  DISALLOW_COPY_AND_ASSIGN(ActionView);
+  DISALLOW_COPY_AND_ASSIGN(MicView);
 };
 
 }  // namespace ash
 
-#endif  // ASH_ASSISTANT_UI_DIALOG_PLATE_ACTION_VIEW_H_
+#endif  // ASH_ASSISTANT_UI_DIALOG_PLATE_MIC_VIEW_H_
diff --git a/ash/assistant/ui/logo_view/base_logo_view.cc b/ash/assistant/ui/logo_view/base_logo_view.cc
index 33f53782..c4eff12 100644
--- a/ash/assistant/ui/logo_view/base_logo_view.cc
+++ b/ash/assistant/ui/logo_view/base_logo_view.cc
@@ -8,7 +8,7 @@
 #include "chromeos/assistant/buildflags.h"
 
 #if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-#include "ash/assistant/ui/logo_view/logo_view.h"
+#include "ash/assistant/ui/logo_view/logo_view_impl.h"
 #endif
 
 namespace ash {
@@ -20,9 +20,10 @@
 // static
 BaseLogoView* BaseLogoView::Create() {
 #if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-  return new LogoView();
-#endif
+  return new LogoViewImpl();
+#else
   return new BaseLogoView();
+#endif
 }
 
 }  // namespace ash
diff --git a/ash/assistant/ui/logo_view/logo_view.cc b/ash/assistant/ui/logo_view/logo_view_impl.cc
similarity index 82%
rename from ash/assistant/ui/logo_view/logo_view.cc
rename to ash/assistant/ui/logo_view/logo_view_impl.cc
index 518760e..4856cf2c 100644
--- a/ash/assistant/ui/logo_view/logo_view.cc
+++ b/ash/assistant/ui/logo_view/logo_view_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/assistant/ui/logo_view/logo_view.h"
+#include "ash/assistant/ui/logo_view/logo_view_impl.h"
 
 #include <algorithm>
 
@@ -24,13 +24,13 @@
   return (timestamp - base::TimeTicks()).InMilliseconds();
 }
 
-int32_t GetLogoAlpha(const LogoView::Logo& logo) {
+int32_t GetLogoAlpha(const LogoViewImpl::Logo& logo) {
   return logo.GetAlpha() * 255;
 }
 
 }  // namespace
 
-LogoView::LogoView()
+LogoViewImpl::LogoViewImpl()
     : mic_part_shape_(chromeos::assistant::kDotDefaultSize),
       state_animator_(&logo_, &state_model_, StateModel::State::kUndefined) {
   SetPaintToLayer();
@@ -41,15 +41,15 @@
                                             &sound_level_input_value_provider_);
 }
 
-LogoView::~LogoView() {
+LogoViewImpl::~LogoViewImpl() {
   state_animator_.StopAnimator();
 }
 
-const char* LogoView::GetClassName() const {
-  return "LogoView";
+const char* LogoViewImpl::GetClassName() const {
+  return "LogoViewImpl";
 }
 
-void LogoView::SetState(BaseLogoView::State state, bool animate) {
+void LogoViewImpl::SetState(BaseLogoView::State state, bool animate) {
   StateModel::State animator_state;
   switch (state) {
     case BaseLogoView::State::kUndefined:
@@ -71,11 +71,11 @@
   state_animator_.SwitchStateTo(animator_state, !animate);
 }
 
-void LogoView::SetSpeechLevel(float speech_level) {
+void LogoViewImpl::SetSpeechLevel(float speech_level) {
   sound_level_input_value_provider_.SetSpeechLevel(speech_level);
 }
 
-int64_t LogoView::StartTimer() {
+int64_t LogoViewImpl::StartTimer() {
   ui::Compositor* compositor = layer()->GetCompositor();
   if (compositor && !compositor->HasAnimationObserver(this)) {
     animating_compositor_ = compositor;
@@ -84,7 +84,7 @@
   return TimeTicksToMs(base::TimeTicks::Now());
 }
 
-void LogoView::StopTimer() {
+void LogoViewImpl::StopTimer() {
   if (animating_compositor_ &&
       animating_compositor_->HasAnimationObserver(this)) {
     animating_compositor_->RemoveAnimationObserver(this);
@@ -92,19 +92,19 @@
   animating_compositor_ = nullptr;
 }
 
-void LogoView::OnAnimationStep(base::TimeTicks timestamp) {
+void LogoViewImpl::OnAnimationStep(base::TimeTicks timestamp) {
   const int64_t current_time_ms = TimeTicksToMs(timestamp);
   state_animator_.OnTimeUpdate(current_time_ms);
   SchedulePaint();
 }
 
-void LogoView::OnCompositingShuttingDown(ui::Compositor* compositor) {
+void LogoViewImpl::OnCompositingShuttingDown(ui::Compositor* compositor) {
   DCHECK(compositor);
   if (animating_compositor_ == compositor)
     StopTimer();
 }
 
-void LogoView::DrawDots(gfx::Canvas* canvas) {
+void LogoViewImpl::DrawDots(gfx::Canvas* canvas) {
   // TODO: The Green Mic parts seems overlapped on the Red Mic part. Draw dots
   // in reverse order so that the Red Mic part is on top of Green Mic parts. But
   // we need to find out why the Mic parts are overlapping in the first place.
@@ -112,7 +112,7 @@
     DrawDot(canvas, (*iter).get());
 }
 
-void LogoView::DrawDot(gfx::Canvas* canvas, Dot* dot) {
+void LogoViewImpl::DrawDot(gfx::Canvas* canvas, Dot* dot) {
   const float radius = dot->GetRadius();
   const float angle = logo_.GetRotation() + dot->GetAngle();
   const float x = radius * std::cos(angle) + dot->GetOffsetX();
@@ -130,7 +130,10 @@
   }
 }
 
-void LogoView::DrawMicPart(gfx::Canvas* canvas, Dot* dot, float x, float y) {
+void LogoViewImpl::DrawMicPart(gfx::Canvas* canvas,
+                               Dot* dot,
+                               float x,
+                               float y) {
   const float progress = dot->GetMicMorph();
   mic_part_shape_.Reset();
   mic_part_shape_.ToMicPart(progress, dot->dot_color());
@@ -138,7 +141,7 @@
   DrawShape(canvas, &mic_part_shape_, dot->color());
 }
 
-void LogoView::DrawShape(gfx::Canvas* canvas, Shape* shape, SkColor color) {
+void LogoViewImpl::DrawShape(gfx::Canvas* canvas, Shape* shape, SkColor color) {
   cc::PaintFlags paint_flags;
   paint_flags.setAntiAlias(true);
   paint_flags.setColor(color);
@@ -153,7 +156,7 @@
   canvas->DrawPath(*shape->second_path(), paint_flags);
 }
 
-void LogoView::DrawLine(gfx::Canvas* canvas, Dot* dot, float x, float y) {
+void LogoViewImpl::DrawLine(gfx::Canvas* canvas, Dot* dot, float x, float y) {
   const float stroke_width = dot->GetSize() * dots_scale_;
   cc::PaintFlags paint_flags;
   paint_flags.setAntiAlias(true);
@@ -171,7 +174,7 @@
                    gfx::PointF(line_x, line_bottom_y), paint_flags);
 }
 
-void LogoView::DrawCircle(gfx::Canvas* canvas, Dot* dot, float x, float y) {
+void LogoViewImpl::DrawCircle(gfx::Canvas* canvas, Dot* dot, float x, float y) {
   const float radius = dot->GetSize() * dot->GetVisibility() / 2.0f;
   cc::PaintFlags paint_flags;
   paint_flags.setAntiAlias(true);
@@ -182,7 +185,7 @@
                      radius * dots_scale_, paint_flags);
 }
 
-void LogoView::OnPaint(gfx::Canvas* canvas) {
+void LogoViewImpl::OnPaint(gfx::Canvas* canvas) {
   View::OnPaint(canvas);
 
   canvas->Save();
@@ -195,7 +198,7 @@
   canvas->Restore();
 }
 
-void LogoView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
+void LogoViewImpl::OnBoundsChanged(const gfx::Rect& previous_bounds) {
   gfx::Rect content_bounds(GetContentsBounds());
   if (content_bounds.IsEmpty())
     return;
@@ -209,7 +212,8 @@
   dots_scale_ = std::fmin(x_scale, y_scale);
 }
 
-void LogoView::VisibilityChanged(views::View* starting_from, bool is_visible) {
+void LogoViewImpl::VisibilityChanged(views::View* starting_from,
+                                     bool is_visible) {
   if (is_visible)
     state_animator_.StartAnimator();
   else
diff --git a/ash/assistant/ui/logo_view/logo_view.h b/ash/assistant/ui/logo_view/logo_view_impl.h
similarity index 86%
rename from ash/assistant/ui/logo_view/logo_view.h
rename to ash/assistant/ui/logo_view/logo_view_impl.h
index 968ec72..da7be86 100644
--- a/ash/assistant/ui/logo_view/logo_view.h
+++ b/ash/assistant/ui/logo_view/logo_view_impl.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_ASSISTANT_UI_LOGO_VIEW_LOGO_VIEW_H_
-#define ASH_ASSISTANT_UI_LOGO_VIEW_LOGO_VIEW_H_
+#ifndef ASH_ASSISTANT_UI_LOGO_VIEW_LOGO_VIEW_IMPL_H_
+#define ASH_ASSISTANT_UI_LOGO_VIEW_LOGO_VIEW_IMPL_H_
 
 #include <cstdint>
 #include <memory>
@@ -43,17 +43,17 @@
 
 // Displays the GLIF (Google Logo and Identity Family). It displays the dots and
 // the google logo. It does not display the Super G.
-class LogoView : public BaseLogoView,
-                 public chromeos::assistant::StateAnimatorTimerDelegate,
-                 public ui::CompositorAnimationObserver {
+class LogoViewImpl : public BaseLogoView,
+                     public chromeos::assistant::StateAnimatorTimerDelegate,
+                     public ui::CompositorAnimationObserver {
  public:
   using Dot = chromeos::assistant::Dot;
   using Logo = chromeos::assistant::Logo;
   using StateAnimator = chromeos::assistant::StateAnimator;
   using StateModel = chromeos::assistant::StateModel;
 
-  LogoView();
-  ~LogoView() override;
+  LogoViewImpl();
+  ~LogoViewImpl() override;
 
   // BaseLogoView:
   const char* GetClassName() const override;
@@ -99,9 +99,9 @@
   chromeos::assistant::SoundLevelInputValueProvider
       sound_level_input_value_provider_;
 
-  DISALLOW_COPY_AND_ASSIGN(LogoView);
+  DISALLOW_COPY_AND_ASSIGN(LogoViewImpl);
 };
 
 }  // namespace ash
 
-#endif  // ASH_ASSISTANT_UI_LOGO_VIEW_LOGO_VIEW_H_
+#endif  // ASH_ASSISTANT_UI_LOGO_VIEW_LOGO_VIEW_IMPL_H_
diff --git a/ash/assistant/util/deep_link_util.cc b/ash/assistant/util/deep_link_util.cc
index 73a1bfa..dcc7c883 100644
--- a/ash/assistant/util/deep_link_util.cc
+++ b/ash/assistant/util/deep_link_util.cc
@@ -32,6 +32,8 @@
 // server. See more details at go/cros-assistant-deeplink.
 constexpr char kChromeSettingsPrefix[] = "googleassistant://chrome-settings";
 constexpr char kAssistantFeedbackPrefix[] = "googleassistant://send-feedback";
+constexpr char kAssistantListsPrefix[] = "googleassistant://lists";
+constexpr char kAssistantNotesPrefix[] = "googleassistant://notes";
 constexpr char kAssistantOnboardingPrefix[] = "googleassistant://onboarding";
 constexpr char kAssistantQueryPrefix[] = "googleassistant://send-query";
 constexpr char kAssistantRemindersPrefix[] = "googleassistant://reminders";
@@ -120,6 +122,8 @@
   static const std::map<DeepLinkType, std::string> kSupportedDeepLinks = {
       {DeepLinkType::kChromeSettings, kChromeSettingsPrefix},
       {DeepLinkType::kFeedback, kAssistantFeedbackPrefix},
+      {DeepLinkType::kLists, kAssistantListsPrefix},
+      {DeepLinkType::kNotes, kAssistantNotesPrefix},
       {DeepLinkType::kOnboarding, kAssistantOnboardingPrefix},
       {DeepLinkType::kQuery, kAssistantQueryPrefix},
       {DeepLinkType::kReminders, kAssistantRemindersPrefix},
@@ -145,15 +149,35 @@
   return GetDeepLinkType(url) != DeepLinkType::kUnsupported;
 }
 
-GURL GetAssistantRemindersUrl(const base::Optional<std::string>& id) {
-  // TODO(b/113357196): Make these URLs configurable for development purposes.
-  static constexpr char kAssistantRemindersWebUrl[] =
-      "https://assistant.google.com/reminders/mainview";
-  static constexpr char kAssistantRemindersByIdWebUrl[] =
-      "https://assistant.google.com/reminders/id/";
+base::Optional<GURL> GetAssistantUrl(DeepLinkType type,
+                                     const base::Optional<std::string>& id) {
+  std::string top_level_url;
+  std::string by_id_url;
+
+  switch (type) {
+    case DeepLinkType::kLists:
+      top_level_url =
+          std::string("https://assistant.google.com/lists/mainview");
+      by_id_url = std::string("https://assistant.google.com/lists/list/");
+      break;
+    case DeepLinkType::kNotes:
+      top_level_url = std::string(
+          "https://assistant.google.com/lists/mainview?note_tap=true");
+      by_id_url = std::string("https://assistant.google.com/lists/note/");
+      break;
+    case DeepLinkType::kReminders:
+      top_level_url =
+          std::string("https://assistant.google.com/reminders/mainview");
+      by_id_url = std::string("https://assistant.google.com/reminders/id/");
+      break;
+    default:
+      NOTREACHED();
+      return base::nullopt;
+  }
+
   return (id && !id.value().empty())
-             ? CreateLocalizedGURL(kAssistantRemindersByIdWebUrl + id.value())
-             : CreateLocalizedGURL(kAssistantRemindersWebUrl);
+             ? CreateLocalizedGURL(by_id_url + id.value())
+             : CreateLocalizedGURL(top_level_url);
 }
 
 GURL GetChromeSettingsUrl(const base::Optional<std::string>& page) {
@@ -178,7 +202,6 @@
 base::Optional<GURL> GetWebUrl(
     DeepLinkType type,
     const std::map<std::string, std::string>& params) {
-  // TODO(b/113357196): Make these URLs configurable for development purposes.
   static constexpr char kAssistantSettingsWebUrl[] =
       "https://assistant.google.com/settings/mainpage";
 
@@ -186,9 +209,12 @@
     return base::nullopt;
 
   switch (type) {
-    case DeepLinkType::kReminders:
-      return GetAssistantRemindersUrl(
-          GetDeepLinkParam(params, DeepLinkParam::kId));
+    case DeepLinkType::kLists:
+    case DeepLinkType::kNotes:
+    case DeepLinkType::kReminders: {
+      const auto id = GetDeepLinkParam(params, DeepLinkParam::kId);
+      return GetAssistantUrl(type, id);
+    }
     case DeepLinkType::kSettings:
       return CreateLocalizedGURL(kAssistantSettingsWebUrl);
     case DeepLinkType::kUnsupported:
@@ -213,8 +239,9 @@
 
 bool IsWebDeepLinkType(DeepLinkType type) {
   // Set of deep link types which open web contents in the Assistant UI.
-  static const std::set<DeepLinkType> kWebDeepLinks = {DeepLinkType::kReminders,
-                                                       DeepLinkType::kSettings};
+  static const std::set<DeepLinkType> kWebDeepLinks = {
+      DeepLinkType::kLists, DeepLinkType::kNotes, DeepLinkType::kReminders,
+      DeepLinkType::kSettings};
 
   return base::ContainsKey(kWebDeepLinks, type);
 }
diff --git a/ash/assistant/util/deep_link_util.h b/ash/assistant/util/deep_link_util.h
index 41ed5ea4..8699c12 100644
--- a/ash/assistant/util/deep_link_util.h
+++ b/ash/assistant/util/deep_link_util.h
@@ -22,6 +22,8 @@
   kUnsupported,
   kChromeSettings,
   kFeedback,
+  kLists,
+  kNotes,
   kOnboarding,
   kQuery,
   kReminders,
@@ -80,10 +82,14 @@
 // Returns true if the specified |url| is a deep link, false otherwise.
 COMPONENT_EXPORT(ASSISTANT_UTIL) bool IsDeepLinkUrl(const GURL& url);
 
-// Returns the URL for the specified Assistant reminder |id|. If id is absent,
-// the returned URL will be for top-level Assistant Reminders.
+// Returns the Assistant URL for the deep link of the specified |type|. A return
+// value will only be present if the deep link type is one of {kLists, kNotes,
+// or kReminders}. If |id| is absent, the returned URL will be for the top-level
+// Assistant URL. Otherwise, the URL will correspond to the resource identified
+// by |id|.
 COMPONENT_EXPORT(ASSISTANT_UTIL)
-GURL GetAssistantRemindersUrl(const base::Optional<std::string>& id);
+base::Optional<GURL> GetAssistantUrl(DeepLinkType type,
+                                     const base::Optional<std::string>& id);
 
 // Returns the URL for the specified Chrome Settings |page|. If page is absent
 // or not allowed, the URL will be for top-level Chrome Settings.
diff --git a/ash/assistant/util/deep_link_util_unittest.cc b/ash/assistant/util/deep_link_util_unittest.cc
index cc2ee92..e75475b3 100644
--- a/ash/assistant/util/deep_link_util_unittest.cc
+++ b/ash/assistant/util/deep_link_util_unittest.cc
@@ -139,6 +139,8 @@
   const std::map<std::string, DeepLinkType> test_cases = {
       // OK: Supported deep links.
       {"googleassistant://chrome-settings", DeepLinkType::kChromeSettings},
+      {"googleassistant://lists", DeepLinkType::kLists},
+      {"googleassistant://notes", DeepLinkType::kNotes},
       {"googleassistant://onboarding", DeepLinkType::kOnboarding},
       {"googleassistant://reminders", DeepLinkType::kReminders},
       {"googleassistant://send-feedback", DeepLinkType::kFeedback},
@@ -151,6 +153,8 @@
       // OK: Parameterized deep links.
       {"googleassistant://chrome-settings?param=true",
        DeepLinkType::kChromeSettings},
+      {"googleassistant://lists?param=true", DeepLinkType::kLists},
+      {"googleassistant://notes?param=true", DeepLinkType::kNotes},
       {"googleassistant://onboarding?param=true", DeepLinkType::kOnboarding},
       {"googleassistant://reminders?param=true", DeepLinkType::kReminders},
       {"googleassistant://send-feedback?param=true", DeepLinkType::kFeedback},
@@ -164,6 +168,8 @@
 
       // UNSUPPORTED: Deep links are case sensitive.
       {"GOOGLEASSISTANT://CHROME-SETTINGS", DeepLinkType::kUnsupported},
+      {"GOOGLEASSISTANT://LISTS", DeepLinkType::kUnsupported},
+      {"GOOGLEASSISTANT://NOTES", DeepLinkType::kUnsupported},
       {"GOOGLEASSISTANT://ONBOARDING", DeepLinkType::kUnsupported},
       {"GOOGLEASSISTANT://REMINDERS", DeepLinkType::kUnsupported},
       {"GOOGLEASSISTANT://SEND-FEEDBACK", DeepLinkType::kUnsupported},
@@ -189,6 +195,8 @@
   const std::map<std::string, DeepLinkType> test_cases = {
       // OK: Supported deep link types.
       {"googleassistant://chrome-settings", DeepLinkType::kChromeSettings},
+      {"googleassistant://lists", DeepLinkType::kLists},
+      {"googleassistant://notes", DeepLinkType::kNotes},
       {"googleassistant://onboarding", DeepLinkType::kOnboarding},
       {"googleassistant://reminders", DeepLinkType::kReminders},
       {"googleassistant://send-feedback", DeepLinkType::kFeedback},
@@ -201,6 +209,8 @@
       // OK: Parameterized deep link types.
       {"googleassistant://chrome-settings?param=true",
        DeepLinkType::kChromeSettings},
+      {"googleassistant://lists?param=true", DeepLinkType::kLists},
+      {"googleassistant://notes?param=true", DeepLinkType::kNotes},
       {"googleassistant://onboarding?param=true", DeepLinkType::kOnboarding},
       {"googleassistant://reminders?param=true", DeepLinkType::kReminders},
       {"googleassistant://send-feedback?param=true", DeepLinkType::kFeedback},
@@ -214,6 +224,8 @@
 
       // UNSUPPORTED: Deep links are case sensitive.
       {"GOOGLEASSISTANT://CHROME-SETTINGS", DeepLinkType::kUnsupported},
+      {"GOOGLEASSISTANT://LISTS", DeepLinkType::kUnsupported},
+      {"GOOGLEASSISTANT://NOTES", DeepLinkType::kUnsupported},
       {"GOOGLEASSISTANT://ONBOARDING", DeepLinkType::kUnsupported},
       {"GOOGLEASSISTANT://REMINDERS", DeepLinkType::kUnsupported},
       {"GOOGLEASSISTANT://SEND-FEEDBACK", DeepLinkType::kUnsupported},
@@ -237,6 +249,8 @@
   const std::map<std::string, bool> test_cases = {
       // OK: Supported deep links.
       {"googleassistant://chrome-settings", true},
+      {"googleassistant://lists", true},
+      {"googleassistant://notes", true},
       {"googleassistant://onboarding", true},
       {"googleassistant://reminders", true},
       {"googleassistant://send-feedback", true},
@@ -248,6 +262,8 @@
 
       // OK: Parameterized deep links.
       {"googleassistant://chrome-settings?param=true", true},
+      {"googleassistant://lists?param=true", true},
+      {"googleassistant://notes?param=true", true},
       {"googleassistant://onboarding?param=true", true},
       {"googleassistant://reminders?param=true", true},
       {"googleassistant://send-feedback?param=true", true},
@@ -259,6 +275,8 @@
 
       // FAIL: Deep links are case sensitive.
       {"GOOGLEASSISTANT://CHROME-SETTINGS", false},
+      {"GOOGLEASSISTANT://LISTS", false},
+      {"GOOGLEASSISTANT://NOTES", false},
       {"GOOGLEASSISTANT://ONBOARDING", false},
       {"GOOGLEASSISTANT://REMINDERS", false},
       {"GOOGLEASSISTANT://SEND-FEEDBACK", false},
@@ -280,21 +298,112 @@
     ASSERT_EQ(test_case.second, IsDeepLinkUrl(GURL(test_case.first)));
 }
 
-TEST_F(DeepLinkUnitTest, GetAssistantRemindersUrl) {
-  const std::map<base::Optional<std::string>, std::string> test_cases = {
-      // OK: Absent/empty id.
-      {base::nullopt,
-       "https://assistant.google.com/reminders/mainview?hl=en-US"},
-      {base::Optional<std::string>(std::string()),
-       "https://assistant.google.com/reminders/mainview?hl=en-US"},
+TEST_F(DeepLinkUnitTest, GetAssistantUrl) {
+  using TestCase = std::pair<DeepLinkType, base::Optional<std::string>>;
 
-      // OK: Specified id.
-      {base::Optional<std::string>("123456"),
-       "https://assistant.google.com/reminders/id/123456?hl=en-US"}};
+  auto CreateTestCase = [](DeepLinkType type, base::Optional<std::string> id) {
+    return std::make_pair(type, id);
+  };
 
-  for (const auto& test_case : test_cases)
-    ASSERT_EQ(test_case.second, GetAssistantRemindersUrl(test_case.first));
-}
+  auto CreateIgnoreCase = [](DeepLinkType type,
+                             base::Optional<std::string> id) {
+    return std::make_pair(std::make_pair(type, id), base::nullopt);
+  };
+
+  const std::map<TestCase, base::Optional<GURL>> test_cases = {
+      // OK: Top-level lists.
+
+      {CreateTestCase(DeepLinkType::kLists, /*id=*/base::nullopt),
+       GURL("https://assistant.google.com/lists/mainview?hl=en-US")},
+
+      {CreateTestCase(DeepLinkType::kLists, /*id=*/std::string()),
+       GURL("https://assistant.google.com/lists/mainview?hl=en-US")},
+
+      // OK: List by |id|.
+
+      {CreateTestCase(DeepLinkType::kLists, /*id=*/"123456"),
+       GURL("https://assistant.google.com/lists/list/123456?hl=en-US")},
+
+      // OK: Top-level notes.
+
+      {CreateTestCase(DeepLinkType::kNotes, /*id=*/base::nullopt),
+       GURL("https://assistant.google.com/lists/"
+            "mainview?note_tap=true&hl=en-US")},
+
+      {CreateTestCase(DeepLinkType::kNotes, /*id=*/std::string()),
+       GURL("https://assistant.google.com/lists/"
+            "mainview?note_tap=true&hl=en-US")},
+
+      // OK: Note by |id|.
+
+      {CreateTestCase(DeepLinkType::kNotes, /*id=*/"123456"),
+       GURL("https://assistant.google.com/lists/note/123456?hl=en-US")},
+
+      // OK: Top-level reminders.
+
+      {CreateTestCase(DeepLinkType::kReminders, /*id=*/base::nullopt),
+       GURL("https://assistant.google.com/reminders/mainview?hl=en-US")},
+
+      {CreateTestCase(DeepLinkType::kReminders, /*id=*/std::string()),
+       GURL("https://assistant.google.com/reminders/mainview?hl=en-US")},
+
+      // OK: Reminder by |id|.
+
+      {CreateTestCase(DeepLinkType::kReminders, /*id=*/"123456"),
+       GURL("https://assistant.google.com/reminders/id/123456?hl=en-US")},
+
+      // IGNORE: Deep links of other types.
+
+      CreateIgnoreCase(DeepLinkType::kUnsupported, /*id=*/base::nullopt),
+      CreateIgnoreCase(DeepLinkType::kUnsupported, /*id=*/std::string()),
+      CreateIgnoreCase(DeepLinkType::kUnsupported, /*id=*/"123456"),
+      CreateIgnoreCase(DeepLinkType::kChromeSettings, /*id=*/base::nullopt),
+      CreateIgnoreCase(DeepLinkType::kChromeSettings, /*id=*/std::string()),
+      CreateIgnoreCase(DeepLinkType::kChromeSettings, /*id=*/"123456"),
+      CreateIgnoreCase(DeepLinkType::kFeedback, /*id=*/base::nullopt),
+      CreateIgnoreCase(DeepLinkType::kFeedback, /*id=*/std::string()),
+      CreateIgnoreCase(DeepLinkType::kFeedback, /*id=*/"123456"),
+      CreateIgnoreCase(DeepLinkType::kOnboarding, /*id=*/base::nullopt),
+      CreateIgnoreCase(DeepLinkType::kOnboarding, /*id=*/std::string()),
+      CreateIgnoreCase(DeepLinkType::kOnboarding, /*id=*/"123456"),
+      CreateIgnoreCase(DeepLinkType::kQuery, /*id=*/base::nullopt),
+      CreateIgnoreCase(DeepLinkType::kQuery, /*id=*/std::string()),
+      CreateIgnoreCase(DeepLinkType::kQuery, /*id=*/"123456"),
+      CreateIgnoreCase(DeepLinkType::kScreenshot, /*id=*/base::nullopt),
+      CreateIgnoreCase(DeepLinkType::kScreenshot, /*id=*/std::string()),
+      CreateIgnoreCase(DeepLinkType::kScreenshot, /*id=*/"123456"),
+      CreateIgnoreCase(DeepLinkType::kSettings, /*id=*/base::nullopt),
+      CreateIgnoreCase(DeepLinkType::kSettings, /*id=*/std::string()),
+      CreateIgnoreCase(DeepLinkType::kSettings, /*id=*/"123456"),
+      CreateIgnoreCase(DeepLinkType::kTaskManager, /*id=*/base::nullopt),
+      CreateIgnoreCase(DeepLinkType::kTaskManager, /*id=*/std::string()),
+      CreateIgnoreCase(DeepLinkType::kTaskManager, /*id=*/"123456"),
+      CreateIgnoreCase(DeepLinkType::kWhatsOnMyScreen, /*id=*/base::nullopt),
+      CreateIgnoreCase(DeepLinkType::kWhatsOnMyScreen, /*id=*/std::string()),
+      CreateIgnoreCase(DeepLinkType::kWhatsOnMyScreen, /*id=*/"123456")};
+
+  // For deep links that are not one of type {kLists, kNotes, kReminders}, we
+  // will hit NOTREACHED since this API isn't meant to be used in such cases.
+  // In unit tests, this would normally result in a DCHECK failure that would
+  // cause the test to fail so we'll use a |ScopedLogAssertHandler| to safely
+  // ignore the NOTREACHED assertion.
+  logging::ScopedLogAssertHandler handler(base::BindRepeating(
+      [](const char* file, int line, const base::StringPiece message,
+         const base::StringPiece stack_trace) {}));
+
+  for (const auto& test_case : test_cases) {
+    const base::Optional<GURL>& expected = test_case.second;
+    const base::Optional<GURL> actual = GetAssistantUrl(
+        /*type=*/test_case.first.first, /*id=*/test_case.first.second);
+
+    // Assert |has_value| equivalence.
+    ASSERT_EQ(expected, actual);
+
+    // Assert |value| equivalence.
+    if (expected)
+      ASSERT_EQ(expected.value(), actual.value());
+  }
+}  // namespace util
 
 TEST_F(DeepLinkUnitTest, GetChromeSettingsUrl) {
   const std::map<base::Optional<std::string>, std::string> test_cases = {
@@ -321,18 +430,28 @@
 TEST_F(DeepLinkUnitTest, GetWebUrl) {
   const std::map<std::string, base::Optional<GURL>> test_cases = {
       // OK: Supported web deep links.
+      {"googleassistant://lists",
+       GURL("https://assistant.google.com/lists/mainview?hl=en-US")},
+      {"googleassistant://notes", GURL("https://assistant.google.com/lists/"
+                                       "mainview?note_tap=true&hl=en-US")},
       {"googleassistant://reminders",
        GURL("https://assistant.google.com/reminders/mainview?hl=en-US")},
       {"googleassistant://settings",
        GURL("https://assistant.google.com/settings/mainpage?hl=en-US")},
 
       // OK: Parameterized deep links.
+      {"googleassistant://lists?id=123456",
+       GURL("https://assistant.google.com/lists/list/123456?hl=en-US")},
+      {"googleassistant://notes?id=123456",
+       GURL("https://assistant.google.com/lists/note/123456?hl=en-US")},
       {"googleassistant://reminders?id=123456",
        GURL("https://assistant.google.com/reminders/id/123456?hl=en-US")},
       {"googleassistant://settings?param=true",
        GURL("https://assistant.google.com/settings/mainpage?hl=en-US")},
 
       // FAIL: Deep links are case sensitive.
+      {"GOOGLEASSISTANT://LISTS", base::nullopt},
+      {"GOOGLEASSISTANT://NOTES", base::nullopt},
       {"GOOGLEASSISTANT://REMINDERS", base::nullopt},
       {"GOOGLEASSISTANT://SETTINGS", base::nullopt},
 
@@ -384,6 +503,17 @@
 
   const std::map<TestCase, base::Optional<GURL>> test_cases = {
       // OK: Supported web deep link types.
+      {CreateTestCase(DeepLinkType::kLists),
+       GURL("https://assistant.google.com/lists/mainview?hl=en-US")},
+      {CreateTestCaseWithParam(DeepLinkType::kLists,
+                               std::make_pair("id", "123456")),
+       GURL("https://assistant.google.com/lists/list/123456?hl=en-US")},
+      {CreateTestCase(DeepLinkType::kNotes),
+       GURL("https://assistant.google.com/lists/"
+            "mainview?note_tap=true&hl=en-US")},
+      {CreateTestCaseWithParam(DeepLinkType::kNotes,
+                               std::make_pair("id", "123456")),
+       GURL("https://assistant.google.com/lists/note/123456?hl=en-US")},
       {CreateTestCase(DeepLinkType::kReminders),
        GURL("https://assistant.google.com/reminders/mainview?hl=en-US")},
       {CreateTestCaseWithParam(DeepLinkType::kReminders,
@@ -421,14 +551,20 @@
 TEST_F(DeepLinkUnitTest, IsWebDeepLink) {
   const std::map<std::string, bool> test_cases = {
       // OK: Supported web deep links.
+      {"googleassistant://lists", true},
+      {"googleassistant://notes", true},
       {"googleassistant://reminders", true},
       {"googleassistant://settings", true},
 
       // OK: Parameterized deep links.
+      {"googleassistant://lists?param=true", true},
+      {"googleassistant://notes?param=true", true},
       {"googleassistant://reminders?param=true", true},
       {"googleassistant://settings?param=true", true},
 
       // FAIL: Deep links are case sensitive.
+      {"GOOGLEASSISTANT://LISTS", false},
+      {"GOOGLEASSISTANT://NOTES", false},
       {"GOOGLEASSISTANT://REMINDERS", false},
       {"GOOGLEASSISTANT://SETTINGS", false},
 
@@ -452,6 +588,8 @@
 TEST_F(DeepLinkUnitTest, IsWebDeepLinkType) {
   const std::map<DeepLinkType, bool> test_cases = {
       // OK: Supported web deep link types.
+      {DeepLinkType::kLists, true},
+      {DeepLinkType::kNotes, true},
       {DeepLinkType::kReminders, true},
       {DeepLinkType::kSettings, true},
 
diff --git a/ash/display/display_error_observer_unittest.cc b/ash/display/display_error_observer_unittest.cc
index 72ea3362..bffa7ce 100644
--- a/ash/display/display_error_observer_unittest.cc
+++ b/ash/display/display_error_observer_unittest.cc
@@ -27,17 +27,17 @@
     const gfx::Size& size,
     display::DisplayConnectionType type) {
   return std::make_unique<display::DisplaySnapshot>(
-      id, /*origin=*/gfx::Point(0, 0), size, type,
-      /*is_aspect_preserving_scaling=*/false, /*has_overscan=*/false,
-      /*has_color_correction_matrix=*/false,
-      /*color_correction_in_linear_space=*/false,
-      /*color_space=*/gfx::ColorSpace(), /*display_name=*/std::string(),
-      /*sys_path=*/base::FilePath(),
-      /*modes=*/display::DisplaySnapshot::DisplayModeList(),
-      /*edid=*/std::vector<uint8_t>(), /*current_mode=*/nullptr,
-      /*native_mode=*/nullptr, /*product_id=*/0,
-      display::kInvalidYearOfManufacture, /*maximum_cursor_size=*/gfx::Size(),
-      /*has_associated_crtc=*/true);
+      id, gfx::Point(0, 0) /* origin */, size, type,
+      false /* is_aspect_preserving_scaling */, false /* has_overscan */,
+      false /* has_color_correction_matrix */,
+      false /* color_correction_in_linear_space */,
+      gfx::ColorSpace() /* color_space */, std::string() /* display_name */,
+      base::FilePath() /* sys_path */,
+      display::DisplaySnapshot::DisplayModeList() /* modes */,
+      std::vector<uint8_t>() /* edid */, nullptr /* current_mode */,
+      nullptr /* native_mode */, 0 /* product_id */,
+      display::kInvalidYearOfManufacture,
+      gfx::Size() /* maximum_cursor_size */);
 }
 
 }  // namespace
diff --git a/ash/extended_desktop_unittest.cc b/ash/extended_desktop_unittest.cc
index 33ea235c..8431fa1 100644
--- a/ash/extended_desktop_unittest.cc
+++ b/ash/extended_desktop_unittest.cc
@@ -732,8 +732,7 @@
 }
 
 TEST_F(ExtendedDesktopTest, OpenSystemTray) {
-  // Two displays side by side and both are high enough for tray bubble.
-  UpdateDisplay("500x600,600x700");
+  UpdateDisplay("500x600,600x400");
   ASSERT_FALSE(IsBubbleShown());
 
   ui::test::EventGenerator* event_generator = GetEventGenerator();
@@ -744,17 +743,9 @@
   event_generator->ClickLeftButton();
   EXPECT_TRUE(IsBubbleShown());
 
-  // Verifies that the bubble is within the primary display.
-  const gfx::Rect& primary_display_bounds = GetPrimaryDisplay().bounds();
-  const gfx::Rect& tray_bubble_bounds =
-      GetPrimaryUnifiedSystemTray()->GetBubbleBoundsInScreen();
-  EXPECT_TRUE(primary_display_bounds.Contains(tray_bubble_bounds))
-      << "primary display bounds=" << primary_display_bounds.ToString()
-      << ", tray bubble bounds=" << tray_bubble_bounds.ToString();
-
   UpdateDisplay("500x600");
   EXPECT_TRUE(IsBubbleShown());
-  UpdateDisplay("500x600,600x700");
+  UpdateDisplay("500x600,600x400");
   EXPECT_TRUE(IsBubbleShown());
 
   // Closes the tray and again makes sure that adding/removing displays doesn't
@@ -766,7 +757,7 @@
 
   UpdateDisplay("500x600");
   EXPECT_FALSE(IsBubbleShown());
-  UpdateDisplay("500x600,600x700");
+  UpdateDisplay("500x600,600x400");
   EXPECT_FALSE(IsBubbleShown());
 }
 
diff --git a/ash/shelf/shelf.cc b/ash/shelf/shelf.cc
index e73e94b..14b9c50 100644
--- a/ash/shelf/shelf.cc
+++ b/ash/shelf/shelf.cc
@@ -308,13 +308,12 @@
   switch (alignment_) {
     case SHELF_ALIGNMENT_BOTTOM:
     case SHELF_ALIGNMENT_BOTTOM_LOCKED:
-      return gfx::Rect(
-          base::i18n::IsRTL() ? work_area.x() : work_area.right() - 1,
-          work_area.bottom() - 1, 0, 0);
+      return gfx::Rect(base::i18n::IsRTL() ? work_area.x() : work_area.right(),
+                       work_area.bottom(), 0, 0);
     case SHELF_ALIGNMENT_LEFT:
-      return gfx::Rect(work_area.x(), work_area.bottom() - 1, 0, 0);
+      return gfx::Rect(work_area.x(), work_area.bottom(), 0, 0);
     case SHELF_ALIGNMENT_RIGHT:
-      return gfx::Rect(work_area.right() - 1, work_area.bottom() - 1, 0, 0);
+      return gfx::Rect(work_area.right(), work_area.bottom(), 0, 0);
   }
   NOTREACHED();
   return gfx::Rect();
diff --git a/ash/system/unified/unified_slider_bubble_controller.cc b/ash/system/unified/unified_slider_bubble_controller.cc
index bd87cbf..3d71cf5a 100644
--- a/ash/system/unified/unified_slider_bubble_controller.cc
+++ b/ash/system/unified/unified_slider_bubble_controller.cc
@@ -168,11 +168,7 @@
   init_params.anchor_view = nullptr;
   init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
   init_params.anchor_rect = tray_->shelf()->GetSystemTrayAnchorRect();
-  // Decrease bottom and right insets to compensate for the adjustment of
-  // the respective edges in Shelf::GetSystemTrayAnchorRect().
-  init_params.insets =
-      gfx::Insets(kUnifiedMenuPadding, kUnifiedMenuPadding,
-                  kUnifiedMenuPadding - 1, kUnifiedMenuPadding - 1);
+  init_params.insets = gfx::Insets(kUnifiedMenuPadding, kUnifiedMenuPadding);
   init_params.corner_radius = kUnifiedTrayCornerRadius;
   init_params.has_shadow = false;
 
diff --git a/ash/system/unified/unified_system_tray_bubble.cc b/ash/system/unified/unified_system_tray_bubble.cc
index b9478d5..115ede03 100644
--- a/ash/system/unified/unified_system_tray_bubble.cc
+++ b/ash/system/unified/unified_system_tray_bubble.cc
@@ -78,11 +78,7 @@
   init_params.anchor_view = nullptr;
   init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
   init_params.anchor_rect = tray->shelf()->GetSystemTrayAnchorRect();
-  // Decrease bottom and right insets to compensate for the adjustment of
-  // the respective edges in Shelf::GetSystemTrayAnchorRect().
-  init_params.insets =
-      gfx::Insets(kUnifiedMenuPadding, kUnifiedMenuPadding,
-                  kUnifiedMenuPadding - 1, kUnifiedMenuPadding - 1);
+  init_params.insets = gfx::Insets(kUnifiedMenuPadding, kUnifiedMenuPadding);
   init_params.corner_radius = kUnifiedTrayCornerRadius;
   init_params.has_shadow = false;
   init_params.show_by_click = show_by_click;
diff --git a/ash/system/unified/unified_system_tray_unittest.cc b/ash/system/unified/unified_system_tray_unittest.cc
index bcb40720..cb2d025 100644
--- a/ash/system/unified/unified_system_tray_unittest.cc
+++ b/ash/system/unified/unified_system_tray_unittest.cc
@@ -7,10 +7,7 @@
 #include "ash/system/status_area_widget.h"
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/system/unified/unified_slider_bubble_controller.h"
-#include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/test/ash_test_base.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
 
 namespace ash {
 
@@ -30,10 +27,6 @@
         ->slider_bubble_controller_->slider_type_;
   }
 
-  UnifiedSystemTrayBubble* GetUnifiedSystemTrayBubble() {
-    return GetPrimaryUnifiedSystemTray()->bubble_.get();
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(UnifiedSystemTrayTest);
 };
@@ -59,26 +52,4 @@
   EXPECT_FALSE(status->ShouldShowShelf());
 }
 
-TEST_F(UnifiedSystemTrayTest, ShowBubble_MultipleDisplays_OpenedOnSameDisplay) {
-  // Initialize two displays with 800x800 resolution.
-  UpdateDisplay("400+400-800x600,1220+400-800x600");
-  auto* screen = display::Screen::GetScreen();
-  EXPECT_EQ(2, screen->GetNumDisplays());
-
-  // The tray bubble for each display should be opened on the same display.
-  // See crbug.com/937420.
-  for (int i = 0; i < screen->GetNumDisplays(); ++i) {
-    auto* system_tray = GetPrimaryUnifiedSystemTray();
-    system_tray->ShowBubble(true /* show_by_click */);
-    const gfx::Rect& primary_display_bounds = GetPrimaryDisplay().bounds();
-    const gfx::Rect& tray_bubble_bounds =
-        GetPrimaryUnifiedSystemTray()->GetBubbleBoundsInScreen();
-    EXPECT_TRUE(primary_display_bounds.Contains(tray_bubble_bounds))
-        << "primary display bounds=" << primary_display_bounds.ToString()
-        << ", tray bubble bounds=" << tray_bubble_bounds.ToString();
-
-    SwapPrimaryDisplay();
-  }
-}
-
 }  // namespace ash
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index 119d363..1ffeacc 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -1203,6 +1203,17 @@
     float opacity,
     const gfx::Rect& work_area,
     OverviewSession::UpdateAnimationSettingsCallback callback) {
+  // Translate the window items to |new_y| with the opacity.
+  for (const auto& window_item : window_list_)
+    window_item->UpdateYPositionAndOpacity(new_y, opacity, callback);
+
+  // Shield widget can be null because it's created asynchronously. The
+  // shield_widget won't use the same transform when created if this
+  // happened. Since shield widget will be removed soon, this is leave it as is.
+  // (https://crbug.com/942759)
+  if (!shield_widget_)
+    return;
+
   // Translate |shield_widget_| to |new_y|. The shield widget covers the shelf
   // so scale it down while moving it, so that it does not cover the launcher,
   // which is showing as this is disappearing.
@@ -1220,10 +1231,6 @@
   shield_window->SetTransform(gfx::Transform(1.f, 0.f, 0.f, height_ratio, 0.f,
                                              static_cast<float>(new_y)));
   shield_window->layer()->SetOpacity(opacity);
-
-  // Apply the same translation and opacity change to the windows in the grid.
-  for (const auto& window_item : window_list_)
-    window_item->UpdateYPositionAndOpacity(new_y, opacity, callback);
 }
 
 aura::Window* OverviewGrid::GetTargetWindowOnLocation(
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 071cf86..f650750 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -302,7 +302,6 @@
   UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowSelector.TimeInOverview",
                              base::Time::Now() - overview_start_time_);
 
-  // Clearing the window list resets the ignored_by_shelf flag on the windows.
   grid_list_.clear();
   UpdateShelfVisibility();
 }
diff --git a/ash/wm/overview/scoped_overview_transform_window.h b/ash/wm/overview/scoped_overview_transform_window.h
index 624c68c1..febea2e 100644
--- a/ash/wm/overview/scoped_overview_transform_window.h
+++ b/ash/wm/overview/scoped_overview_transform_window.h
@@ -201,9 +201,6 @@
   // A weak pointer to the real window in the overview.
   aura::Window* window_;
 
-  // Tracks if this window was ignored by the shelf.
-  bool ignored_by_shelf_;
-
   // True if the window has been transformed for overview mode.
   bool overview_started_ = false;
 
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
index faa2c4fa..b0eed08 100644
--- a/ash/wm/workspace/workspace_window_resizer.cc
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -890,26 +890,26 @@
   if (!window_state()->CanResize())
     return false;
 
-  for (aura::Window* root_window : Shell::Get()->GetAllRootWindows()) {
-    // Test all children from the desktop in each root window.
-    const std::vector<aura::Window*>& children =
-        root_window->GetChildById(kShellWindowId_DefaultContainer)->children();
-    for (auto i = children.rbegin();
-         i != children.rend() && !matcher.AreEdgesObscured(); ++i) {
-      wm::WindowState* other_state = wm::GetWindowState(*i);
-      if (other_state->window() == GetTarget() ||
-          !other_state->window()->IsVisible() ||
-          !other_state->IsNormalOrSnapped() || !other_state->CanResize()) {
-        continue;
-      }
-      if (matcher.ShouldAttach(other_state->window()->GetBoundsInScreen(),
-                               &magnetism_edge_)) {
-        magnetism_window_ = other_state->window();
-        window_tracker_.Add(magnetism_window_);
-        return true;
-      }
+  aura::Window* root_window = GetTarget()->GetRootWindow();
+  DCHECK(root_window);
+  const std::vector<aura::Window*>& children =
+      root_window->GetChildById(kShellWindowId_DefaultContainer)->children();
+  for (auto i = children.rbegin();
+       i != children.rend() && !matcher.AreEdgesObscured(); ++i) {
+    wm::WindowState* other_state = wm::GetWindowState(*i);
+    if (other_state->window() == GetTarget() ||
+        !other_state->window()->IsVisible() ||
+        !other_state->IsNormalOrSnapped() || !other_state->CanResize()) {
+      continue;
+    }
+    if (matcher.ShouldAttach(other_state->window()->GetBoundsInScreen(),
+                             &magnetism_edge_)) {
+      magnetism_window_ = other_state->window();
+      window_tracker_.Add(magnetism_window_);
+      return true;
     }
   }
+
   return false;
 }
 
diff --git a/base/android/java/src/org/chromium/base/task/PostTask.java b/base/android/java/src/org/chromium/base/task/PostTask.java
index 0b6b30d..a5f7e3b 100644
--- a/base/android/java/src/org/chromium/base/task/PostTask.java
+++ b/base/android/java/src/org/chromium/base/task/PostTask.java
@@ -10,6 +10,7 @@
 import java.util.Collections;
 import java.util.Set;
 import java.util.WeakHashMap;
+import java.util.concurrent.Executor;
 
 /**
  * Java interface to the native chromium scheduler.  Note tasks can be posted before native
@@ -21,7 +22,8 @@
     private static final Object sLock = new Object();
     private static Set<TaskRunner> sPreNativeTaskRunners =
             Collections.newSetFromMap(new WeakHashMap<TaskRunner, Boolean>());
-
+    private static final Executor sPrenativeThreadPoolExecutor = new ChromeThreadPoolExecutor();
+    private static Executor sPrenativeThreadPoolExecutorOverride;
     private static final TaskExecutor sTaskExecutors[] = getInitialTaskExecutors();
 
     private static TaskExecutor[] getInitialTaskExecutors() {
@@ -123,6 +125,37 @@
     }
 
     /**
+     * Lets a test override the pre-native thread pool executor.
+     *
+     * @param executor The Executor to use for pre-native thread pool tasks.
+     */
+    public static void setPrenativeThreadPoolExecutorForTesting(Executor executor) {
+        synchronized (sLock) {
+            sPrenativeThreadPoolExecutorOverride = executor;
+        }
+    }
+
+    /**
+     * Clears an override set by setPrenativeThreadPoolExecutorOverrideForTesting.
+     */
+    public static void resetPrenativeThreadPoolExecutorForTesting() {
+        synchronized (sLock) {
+            sPrenativeThreadPoolExecutorOverride = null;
+        }
+    }
+
+    /**
+     * @return The current Executor that PrenativeThreadPool tasks should run on.
+     */
+    static Executor getPrenativeThreadPoolExecutor() {
+        synchronized (sLock) {
+            if (sPrenativeThreadPoolExecutorOverride != null)
+                return sPrenativeThreadPoolExecutorOverride;
+            return sPrenativeThreadPoolExecutor;
+        }
+    }
+
+    /**
      * Called by every TaskRunner on its creation, attempts to register this
      * TaskRunner as pre-native, unless the native scheduler has been
      * initialised already, and informs the caller about the outcome. Called
diff --git a/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java b/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
index dad7384..548aa51 100644
--- a/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
+++ b/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
@@ -30,8 +30,6 @@
     protected final Runnable mRunPreNativeTaskClosure = this::runPreNativeTask;
     private boolean mIsDestroying;
     private final LifetimeAssert mLifetimeAssert = LifetimeAssert.create(this);
-    private static final ChromeThreadPoolExecutor THREAD_POOL_EXECUTOR =
-            new ChromeThreadPoolExecutor();
 
     @Nullable
     protected LinkedList<Runnable> mPreNativeTasks = new LinkedList<>();
@@ -106,7 +104,7 @@
      * time.
      */
     protected void schedulePreNativeTask() {
-        THREAD_POOL_EXECUTOR.execute(mRunPreNativeTaskClosure);
+        PostTask.getPrenativeThreadPoolExecutor().execute(mRunPreNativeTaskClosure);
     }
 
     /**
diff --git a/base/android/java/templates/BuildConfig.template b/base/android/java/templates/BuildConfig.template
index 8e7342c..2d8331b 100644
--- a/base/android/java/templates/BuildConfig.template
+++ b/base/android/java/templates/BuildConfig.template
@@ -71,4 +71,17 @@
     // Default value, do not use.
     public static MAYBE_FINAL int R_STRING_PRODUCT_VERSION MAYBE_ZERO;
 #endif
+
+    // Minimum SDK Version supported by this apk.
+    // Be cautious when using this value, as it can happen that older apks get
+    // installed on newer Android version (e.g. when a device goes through a
+    // system upgrade). It is also convenient for developing to have all
+    // features available through a single APK.
+    // However, it's pretty safe to assument that a feature specific to KitKat
+    // will never be needed in an APK with MIN_SDK_VERSION = Oreo.
+#if defined(_MIN_SDK_VERSION)
+    public static MAYBE_FINAL int MIN_SDK_VERSION = _MIN_SDK_VERSION;
+#else
+    public static MAYBE_FINAL int MIN_SDK_VERSION = 1;
+#endif
 }
diff --git a/base/json/json_writer.cc b/base/json/json_writer.cc
index 376a459f..cd020e7 100644
--- a/base/json/json_writer.cc
+++ b/base/json/json_writer.cc
@@ -179,6 +179,11 @@
       // Successful only if we're allowed to omit it.
       DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value.";
       return omit_binary_values_;
+
+    // TODO(crbug.com/859477): Remove after root cause is found.
+    case Value::Type::DEAD:
+      CHECK(false);
+      return false;
   }
 
   // TODO(crbug.com/859477): Revert to NOTREACHED() after root cause is found.
diff --git a/base/parameter_pack.h b/base/parameter_pack.h
index 95a6f6b5..8fec8fba 100644
--- a/base/parameter_pack.h
+++ b/base/parameter_pack.h
@@ -91,6 +91,9 @@
   // Helper for extracting the Nth type from a parameter pack.
   template <size_t N>
 #if defined(__clang__) && !defined(OS_NACL)
+  // A clang extension which efficiently returns the Nth type from a pack. This
+  // is faster to compile than std::tuple_element<>.
+  // See: https://ldionne.com/2015/11/29/efficient-parameter-pack-indexing/
   using NthType = __type_pack_element<N, Ts...>;
 #else
   using NthType = typename std::tuple_element<N, std::tuple<Ts...>>::type;
diff --git a/base/profiler/native_stack_sampler_mac.cc b/base/profiler/native_stack_sampler_mac.cc
index ad92f40..907a6a0a 100644
--- a/base/profiler/native_stack_sampler_mac.cc
+++ b/base/profiler/native_stack_sampler_mac.cc
@@ -258,10 +258,8 @@
  private:
   // Walks the stack represented by |thread_state|, calling back to the
   // provided lambda for each frame.
-  template <typename StackFrameCallback>
-  void WalkStack(const x86_thread_state64_t& thread_state,
-                 uintptr_t stack_top,
-                 const StackFrameCallback& callback);
+  std::vector<Frame> WalkStack(const x86_thread_state64_t& thread_state,
+                               uintptr_t stack_top);
 
   // Weak reference: Mach port for thread being profiled.
   mach_port_t thread_port_;
@@ -348,35 +346,19 @@
     test_delegate_->OnPreStackWalk();
 
   // Walk the stack and record it.
-
-  // Avoid an out-of-bounds read bug in libunwind that can crash us in some
-  // circumstances. If we're subject to that case, just record the first frame
-  // and bail. See MayTriggerUnwInitLocalCrash for details.
-  uintptr_t rip = thread_state.__rip;
-  const ModuleCache::Module* leaf_frame_module =
-      module_cache_->GetModuleForAddress(rip);
-  if (leaf_frame_module && MayTriggerUnwInitLocalCrash(leaf_frame_module)) {
-    profile_builder->OnSampleCompleted({Frame(rip, leaf_frame_module)});
-    return;
-  }
-
-  // Reserve enough memory for most stacks, to avoid repeated allocations.
-  // Approximately 99.9% of recorded stacks are 128 frames or fewer.
-  std::vector<Frame> frames;
-  frames.reserve(128);
-
-  WalkStack(thread_state, new_stack_top,
-            [&frames](uintptr_t frame_ip, const ModuleCache::Module* module) {
-              frames.emplace_back(frame_ip, module);
-            });
-
-  profile_builder->OnSampleCompleted(frames);
+  profile_builder->OnSampleCompleted(WalkStack(thread_state, new_stack_top));
 }
 
-template <typename StackFrameCallback>
-void NativeStackSamplerMac::WalkStack(const x86_thread_state64_t& thread_state,
-                                      uintptr_t stack_top,
-                                      const StackFrameCallback& callback) {
+std::vector<Frame> NativeStackSamplerMac::WalkStack(
+    const x86_thread_state64_t& thread_state,
+    uintptr_t stack_top) {
+  std::vector<Frame> stack;
+
+  // Reserve enough memory for most stacks, to avoid repeated
+  // allocations. Approximately 99.9% of recorded stacks are 128 frames or
+  // fewer.
+  stack.reserve(128);
+
   // There isn't an official way to create a unw_context other than to create it
   // from the current state of the current thread's stack. Since we're walking a
   // different thread's stack we must forge a context. The unw_context is just a
@@ -387,6 +369,15 @@
   unw_context_t unwind_context;
   memcpy(&unwind_context, &thread_state, sizeof(uintptr_t) * 17);
 
+  // Avoid an out-of-bounds read bug in libunwind that can crash us in some
+  // circumstances. If we're subject to that case, just record the first frame
+  // and bail. See MayTriggerUnwInitLocalCrash for details.
+  const ModuleCache::Module* leaf_frame_module =
+      module_cache_->GetModuleForAddress(thread_state.__rip);
+  if (leaf_frame_module && MayTriggerUnwInitLocalCrash(leaf_frame_module)) {
+    return {Frame(thread_state.__rip, leaf_frame_module)};
+  }
+
   unw_cursor_t unwind_cursor;
   unw_init_local(&unwind_cursor, &unwind_context);
 
@@ -410,9 +401,10 @@
     const ModuleCache::Module* module =
         module_cache_->GetModuleForAddress(instruction_pointer);
     if (!module)
-      return;
+      break;
 
-    callback(static_cast<uintptr_t>(instruction_pointer), module);
+    // Record the frame.
+    stack.emplace_back(instruction_pointer, module);
 
     // Don't continue if we're in sigtramp. Unwinding this from another thread
     // is very fragile. It's a complex DWARF unwind that needs to restore the
@@ -420,12 +412,12 @@
     // occurred.
     if (instruction_pointer >= sigtramp_start_ &&
         instruction_pointer < sigtramp_end_)
-      return;
+      break;
 
     // Don't continue if rbp appears to be invalid (due to a previous bad
     // unwind).
     if (!HasValidRbp(&unwind_cursor, stack_top))
-      return;
+      break;
 
     step_result = unw_step(&unwind_cursor);
 
@@ -453,6 +445,8 @@
 
     at_top_frame = false;
   } while (step_result > 0);
+
+  return stack;
 }
 
 // NativeStackSampler ---------------------------------------------------------
diff --git a/base/values.cc b/base/values.cc
index 2b0c6c8..035aa235 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -90,8 +90,6 @@
 
 }  // namespace
 
-constexpr uint16_t Value::kMagicIsAlive;
-
 // static
 std::unique_ptr<Value> Value::CreateWithCopiedBuffer(const char* buffer,
                                                      size_t size) {
@@ -112,9 +110,9 @@
   InternalMoveConstructFrom(std::move(that));
 }
 
-Value::Value() noexcept : type_(Type::NONE), is_alive_(kMagicIsAlive) {}
+Value::Value() noexcept : type_(Type::NONE) {}
 
-Value::Value(Type type) : type_(type), is_alive_(kMagicIsAlive) {
+Value::Value(Type type) : type_(type) {
   // Initialize with the default value.
   switch (type_) {
     case Type::NONE:
@@ -141,22 +139,26 @@
     case Type::LIST:
       new (&list_) ListStorage();
       return;
+    // TODO(crbug.com/859477): Remove after root cause is found.
+    case Type::DEAD:
+      CHECK(false);
+      return;
   }
+
+  // TODO(crbug.com/859477): Revert to NOTREACHED() after root cause is found.
+  CHECK(false);
 }
 
 Value::Value(bool in_bool)
     : bool_type_(Type::BOOLEAN),
-      bool_is_alive_(kMagicIsAlive),
       bool_value_(in_bool) {}
 
 Value::Value(int in_int)
     : int_type_(Type::INTEGER),
-      int_is_alive_(kMagicIsAlive),
       int_value_(in_int) {}
 
 Value::Value(double in_double)
     : double_type_(Type::DOUBLE),
-      double_is_alive_(kMagicIsAlive),
       double_value_(in_double) {
   if (!std::isfinite(double_value_)) {
     NOTREACHED() << "Non-finite (i.e. NaN or positive/negative infinity) "
@@ -171,7 +173,6 @@
 
 Value::Value(std::string&& in_string) noexcept
     : string_type_(Type::STRING),
-      string_is_alive_(kMagicIsAlive),
       string_value_(std::move(in_string)) {
   DCHECK(IsStringUTF8(string_value_));
 }
@@ -182,21 +183,18 @@
 
 Value::Value(const std::vector<char>& in_blob)
     : binary_type_(Type::BINARY),
-      binary_is_alive_(kMagicIsAlive),
       binary_value_(in_blob.begin(), in_blob.end()) {}
 
 Value::Value(base::span<const uint8_t> in_blob)
     : binary_type_(Type::BINARY),
-      binary_is_alive_(kMagicIsAlive),
       binary_value_(in_blob.begin(), in_blob.end()) {}
 
 Value::Value(BlobStorage&& in_blob) noexcept
     : binary_type_(Type::BINARY),
-      binary_is_alive_(kMagicIsAlive),
       binary_value_(std::move(in_blob)) {}
 
 Value::Value(const DictStorage& in_dict)
-    : dict_type_(Type::DICTIONARY), dict_is_alive_(kMagicIsAlive), dict_() {
+    : dict_type_(Type::DICTIONARY), dict_() {
   dict_.reserve(in_dict.size());
   for (const auto& it : in_dict) {
     dict_.try_emplace(dict_.end(), it.first,
@@ -206,11 +204,9 @@
 
 Value::Value(DictStorage&& in_dict) noexcept
     : dict_type_(Type::DICTIONARY),
-      dict_is_alive_(kMagicIsAlive),
       dict_(std::move(in_dict)) {}
 
-Value::Value(const ListStorage& in_list)
-    : list_type_(Type::LIST), list_is_alive_(kMagicIsAlive), list_() {
+Value::Value(const ListStorage& in_list) : list_type_(Type::LIST), list_() {
   list_.reserve(in_list.size());
   for (const auto& val : in_list)
     list_.emplace_back(val.Clone());
@@ -218,7 +214,6 @@
 
 Value::Value(ListStorage&& in_list) noexcept
     : list_type_(Type::LIST),
-      list_is_alive_(kMagicIsAlive),
       list_(std::move(in_list)) {}
 
 Value& Value::operator=(Value&& that) noexcept {
@@ -246,15 +241,21 @@
       return Value(dict_);
     case Type::LIST:
       return Value(list_);
+      // TODO(crbug.com/859477): Remove after root cause is found.
+    case Type::DEAD:
+      CHECK(false);
+      return Value();
   }
 
-  NOTREACHED();
+  // TODO(crbug.com/859477): Revert to NOTREACHED() after root cause is found.
+  CHECK(false);
   return Value();
 }
 
 Value::~Value() {
   InternalCleanup();
-  is_alive_ = 0;
+  // TODO(crbug.com/859477): Remove after root cause is found.
+  type_ = Type::DEAD;
 }
 
 // static
@@ -353,7 +354,7 @@
   return dict_.erase(key) != 0;
 }
 
-Value* Value::SetKey(StringPiece key, Value value) {
+Value* Value::SetKey(StringPiece key, Value&& value) {
   CHECK(is_dict());
   // NOTE: We can't use |insert_or_assign| here, as only |try_emplace| does
   // an explicit conversion from StringPiece to std::string if necessary.
@@ -366,7 +367,7 @@
   return result.first->second.get();
 }
 
-Value* Value::SetKey(std::string&& key, Value value) {
+Value* Value::SetKey(std::string&& key, Value&& value) {
   CHECK(is_dict());
   return dict_
       .insert_or_assign(std::move(key),
@@ -374,7 +375,7 @@
       .first->second.get();
 }
 
-Value* Value::SetKey(const char* key, Value value) {
+Value* Value::SetKey(const char* key, Value&& value) {
   return SetKey(StringPiece(key), std::move(value));
 }
 
@@ -425,12 +426,12 @@
   return result;
 }
 
-Value* Value::SetPath(std::initializer_list<StringPiece> path, Value value) {
+Value* Value::SetPath(std::initializer_list<StringPiece> path, Value&& value) {
   DCHECK_GE(path.size(), 2u) << "Use SetKey() for a path of length 1.";
   return SetPath(make_span(path.begin(), path.size()), std::move(value));
 }
 
-Value* Value::SetPath(span<const StringPiece> path, Value value) {
+Value* Value::SetPath(span<const StringPiece> path, Value&& value) {
   DCHECK(path.begin() != path.end());  // Can't be empty path.
 
   // Walk/construct intermediate dictionaries. The last element requires
@@ -654,9 +655,14 @@
                         });
     case Value::Type::LIST:
       return lhs.list_ == rhs.list_;
+      // TODO(crbug.com/859477): Remove after root cause is found.
+    case Value::Type::DEAD:
+      CHECK(false);
+      return false;
   }
 
-  NOTREACHED();
+  // TODO(crbug.com/859477): Revert to NOTREACHED() after root cause is found.
+  CHECK(false);
   return false;
 }
 
@@ -693,9 +699,14 @@
           });
     case Value::Type::LIST:
       return lhs.list_ < rhs.list_;
+      // TODO(crbug.com/859477): Remove after root cause is found.
+    case Value::Type::DEAD:
+      CHECK(false);
+      return false;
   }
 
-  NOTREACHED();
+  // TODO(crbug.com/859477): Revert to NOTREACHED() after root cause is found.
+  CHECK(false);
   return false;
 }
 
@@ -733,7 +744,6 @@
 
 void Value::InternalMoveConstructFrom(Value&& that) {
   type_ = that.type_;
-  is_alive_ = that.is_alive_;
 
   switch (type_) {
     case Type::NONE:
@@ -759,12 +769,17 @@
     case Type::LIST:
       new (&list_) ListStorage(std::move(that.list_));
       return;
+      // TODO(crbug.com/859477): Remove after root cause is found.
+    case Type::DEAD:
+      CHECK(false);
+      return;
   }
+
+  // TODO(crbug.com/859477): Revert to NOTREACHED() after root cause is found.
+  CHECK(false);
 }
 
 void Value::InternalCleanup() {
-  CHECK_EQ(is_alive_, kMagicIsAlive);
-
   switch (type_) {
     case Type::NONE:
     case Type::BOOLEAN:
@@ -785,7 +800,14 @@
     case Type::LIST:
       list_.~ListStorage();
       return;
+      // TODO(crbug.com/859477): Remove after root cause is found.
+    case Type::DEAD:
+      CHECK(false);
+      return;
   }
+
+  // TODO(crbug.com/859477): Revert to NOTREACHED() after root cause is found.
+  CHECK(false);
 }
 
 ///////////////////// DictionaryValue ////////////////////
diff --git a/base/values.h b/base/values.h
index 7546fa5..e31cadd8 100644
--- a/base/values.h
+++ b/base/values.h
@@ -92,7 +92,9 @@
     STRING,
     BINARY,
     DICTIONARY,
-    LIST
+    LIST,
+    // TODO(crbug.com/859477): Remove once root cause is found.
+    DEAD
     // Note: Do not add more types. See the file-level comment above for why.
   };
 
@@ -210,11 +212,11 @@
   //
   // Example:
   //   SetKey("foo", std::move(myvalue));
-  Value* SetKey(StringPiece key, Value value);
+  Value* SetKey(StringPiece key, Value&& value);
   // This overload results in a performance improvement for std::string&&.
-  Value* SetKey(std::string&& key, Value value);
+  Value* SetKey(std::string&& key, Value&& value);
   // This overload is necessary to avoid ambiguity for const char* arguments.
-  Value* SetKey(const char* key, Value value);
+  Value* SetKey(const char* key, Value&& value);
 
   // This attemps to remove the value associated with |key|. In case of failure,
   // e.g. the key does not exist, |false| is returned and the underlying
@@ -276,8 +278,8 @@
   //   value.SetPath(components, std::move(myvalue));
   //
   // Note: If there is only one component in the path, use SetKey() instead.
-  Value* SetPath(std::initializer_list<StringPiece> path, Value value);
-  Value* SetPath(span<const StringPiece> path, Value value);
+  Value* SetPath(std::initializer_list<StringPiece> path, Value&& value);
+  Value* SetPath(span<const StringPiece> path, Value&& value);
 
   // Tries to remove a Value at the given path.
   //
@@ -375,10 +377,6 @@
   size_t EstimateMemoryUsage() const;
 
  protected:
-  // Magic IsAlive signature to debug double frees.
-  // TODO(crbug.com/859477): Remove once root cause is found.
-  static constexpr uint16_t kMagicIsAlive = 0x2f19;
-
   // Technical note:
   // The naive way to implement a tagged union leads to wasted bytes
   // in the object on CPUs like ARM ones, which impose an 8-byte alignment
@@ -408,8 +406,8 @@
   // that |double_value_| below is always located at an offset that is a
   // multiple of 8, relative to the start of the overall data structure.
   //
-  // Each struct must declare its own |type_| and |is_alive_| field, which
-  // must have a different name, to appease the C++ compiler.
+  // Each struct must declare its own |type_| field, which must have a different
+  // name, to appease the C++ compiler.
   //
   // Using this technique sizeof(base::Value) == 16 on 32-bit ARM instead
   // of 24, without losing any information. Results are unchanged for x86,
@@ -419,24 +417,17 @@
       // TODO(crbug.com/646113): Make these private once DictionaryValue and
       // ListValue are properly inlined.
       Type type_ : 8;
-
-      // IsAlive member to debug double frees.
-      // TODO(crbug.com/859477): Remove once root cause is found.
-      uint16_t is_alive_ = kMagicIsAlive;
     };
     struct {
       Type bool_type_ : 8;
-      uint16_t bool_is_alive_;
       bool bool_value_;
     };
     struct {
       Type int_type_ : 8;
-      uint16_t int_is_alive_;
       int int_value_;
     };
     struct {
       Type double_type_ : 8;
-      uint16_t double_is_alive_;
       // Subtle: On architectures that require it, the compiler will ensure
       // that |double_value_|'s offset is a multiple of 8 (e.g. 32-bit ARM).
       // See technical note above to understand why it is important.
@@ -444,22 +435,18 @@
     };
     struct {
       Type string_type_ : 8;
-      uint16_t string_is_alive_;
       std::string string_value_;
     };
     struct {
       Type binary_type_ : 8;
-      uint16_t binary_is_alive_;
       BlobStorage binary_value_;
     };
     struct {
       Type dict_type_ : 8;
-      uint16_t dict_is_alive_;
       DictStorage dict_;
     };
     struct {
       Type list_type_ : 8;
-      uint16_t list_is_alive_;
       ListStorage list_;
     };
   };
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
index 0a641bcc..b23fd83 100644
--- a/base/values_unittest.cc
+++ b/base/values_unittest.cc
@@ -20,17 +20,20 @@
 #include "base/strings/string16.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
 
+// Test is currently incorrect on Windows x86.
+#if !defined(OS_WIN) || !defined(ARCH_CPU_X86)
 TEST(ValuesTest, SizeOfValue) {
   // Ensure that base::Value is as small as possible, i.e. that there is
   // no wasted space after the inner value due to alignment constraints.
-  // Distinguish between the 'header' that includes |type_| and |is_alive_|
-  // and the inner value that follows it, which can be a bool, int, double,
-  // string, blob, list or dict.
+  // Distinguish between the 'header' that includes |type_| and and the inner
+  // value that follows it, which can be a bool, int, double, string, blob, list
+  // or dict.
 #define INNER_TYPES_LIST(X)            \
   X(bool, bool_value_)                 \
   X(int, int_value_)                   \
@@ -61,6 +64,7 @@
     LOG(INFO) << "max_inner_struct_limit=" << max_inner_struct_limit;
   }
 }
+#endif
 
 TEST(ValuesTest, TestNothrow) {
   static_assert(std::is_nothrow_move_constructible<Value>::value,
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index bf4dfa0..a5ed9801 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1918,6 +1918,7 @@
   #   build_config: Path to build_config used for locale list
   #   enable_multidex: Value for ENABLE_MULTIDEX.
   #   firebase_app_id: Value for FIREBASE_APP_ID.
+  #   min_sdk_version: Value for MIN_SDK_VERSION.
   #
   template("generate_build_config_srcjar") {
     java_cpp_template(target_name) {
@@ -1962,6 +1963,9 @@
         if (defined(invoker.firebase_app_id)) {
           defines += [ "_FIREBASE_APP_ID=${invoker.firebase_app_id}" ]
         }
+        if (defined(invoker.min_sdk_version)) {
+          defines += [ "_MIN_SDK_VERSION=${invoker.min_sdk_version}" ]
+        }
         if (defined(invoker.resources_version_variable)) {
           defines += [
             "_RESOURCES_VERSION_VARIABLE=${invoker.resources_version_variable}",
@@ -2524,7 +2528,11 @@
 
     if (_generate_buildconfig_java) {
       generate_build_config_srcjar("${_template_name}__build_config_srcjar") {
-        forward_variables_from(invoker, [ "firebase_app_id" ])
+        forward_variables_from(invoker,
+                               [
+                                 "firebase_app_id",
+                                 "min_sdk_version",
+                               ])
         use_final_fields = true
         build_config = _build_config
         enable_multidex = _enable_multidex
diff --git a/build/config/c++/c++.gni b/build/config/c++/c++.gni
index 61f07bc..5d45ff1 100644
--- a/build/config/c++/c++.gni
+++ b/build/config/c++/c++.gni
@@ -10,8 +10,10 @@
   # standard library support.
   # Don't check in changes that set this to false for more platforms; doing so
   # is not supported.
+  # TODO(https://crbug.com/942939): Re-enable libc++ on Windows with libfuzzer.
   use_custom_libcxx =
       is_fuchsia || is_android || is_mac ||
+      (is_win && is_clang && !use_libfuzzer) ||
       (is_linux &&
        (!is_chromeos || default_toolchain != "//build/toolchain/cros:target"))
 
diff --git a/build/config/chromecast_build.gni b/build/config/chromecast_build.gni
index 61870e4a..13d4b43 100644
--- a/build/config/chromecast_build.gni
+++ b/build/config/chromecast_build.gni
@@ -16,6 +16,10 @@
 
   # Set this true for an audio-only Chromecast build.
   is_cast_audio_only = false
+
+  # If true, use cast CMA backend instead of default chromium media pipeline.
+  # TODO(sanfin): Remove this flag when all builds enable CMA.
+  is_cast_using_cma_backend = true
 }
 
 # Note(slan): This arg depends on the value of is_chromecast, and thus must be
@@ -31,6 +35,36 @@
                           (target_cpu == "x86" || target_cpu == "x64")
 }
 
+declare_args() {
+  # True to enable the cast renderer.  It is enabled by default for non-android
+  # builds.
+  enable_cast_renderer =
+      is_chromecast && is_cast_using_cma_backend && !is_android
+}
+
+# Configures media options for cast.  See media/media_options.gni
+cast_mojo_media_services = []
+cast_mojo_media_host = "none"
+
+if (enable_cast_renderer) {
+  cast_mojo_media_services = [
+    "cdm",
+    "renderer",
+  ]
+  cast_mojo_media_host = "browser"
+} else if (is_android) {
+  cast_mojo_media_services = [
+    "cdm",
+    "audio_decoder",
+  ]
+  if (is_cast_audio_only) {
+    cast_mojo_media_host = "browser"
+  } else {
+    cast_mojo_media_services += [ "video_decoder" ]
+    cast_mojo_media_host = "gpu"
+  }
+}
+
 # Assert that Chromecast is being built for a supported platform.
 assert(is_linux || is_android || is_fuchsia || !is_chromecast,
        "Chromecast builds are not supported on $target_os")
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index d54752d..f068e642 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-4a56f4ef93ff9a4556ceedf77b29b898aee8cecd
\ No newline at end of file
+37d4d95da14b3c8cdde7d27afd6a11333832266d
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 6877fa88..c0f51f6 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-fa8802142b8e9bab857b2c4fa19c44b3a3d17369
\ No newline at end of file
+436c2e2b3588138fe1b5c96950827450aacd0c86
\ No newline at end of file
diff --git a/buildtools/DEPS b/buildtools/DEPS
index 89fab254..71eb07a3 100644
--- a/buildtools/DEPS
+++ b/buildtools/DEPS
@@ -4,7 +4,7 @@
   'chromium_url': 'https://chromium.googlesource.com',
 
   #
-  # TODO(crbug.com/941824): These revisions need to be kept in sync
+  # TODO(crbug.com/941824): The values below need to be kept in sync
   # between //DEPS and //buildtools/DEPS, so if you're updating one,
   # update the other. There is a presubmit check that checks that
   # you've done so; if you are adding new tools to //buildtools and
@@ -13,6 +13,9 @@
   # revisions.
   #
 
+  # GN CIPD package version.
+  'gn_version': 'git_revision:0790d3043387c762a6bacb1ae0a9ebe883188ab2',
+
   # When changing these, also update the svn revisions in deps_revisions.gni
   'clang_format_revision': '96636aa0e9f047f17447f2d45a094d0b59ed7917',
   'libcxx_revision':       'a50f5035629b7621e92acef968403f71b7d48553',
@@ -24,6 +27,26 @@
   'clang_format/script':
     Var('chromium_url') + '/chromium/llvm-project/cfe/tools/clang-format.git@' +
     Var('clang_format_revision'),
+  'linux64': {
+    'packages': [
+      {
+        'package': 'gn/gn/linux-amd64',
+        'version': Var('gn_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'checkout_linux',
+  },
+  'mac': {
+    'packages': [
+      {
+        'package': 'gn/gn/mac-amd64',
+        'version': Var('gn_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'checkout_mac',
+  },
   'third_party/libc++/trunk':
     Var('chromium_url') + '/chromium/llvm-project/libcxx.git' + '@' +
     Var('libcxx_revision'),
@@ -33,4 +56,14 @@
   'third_party/libunwind/trunk':
     Var('chromium_url') + '/external/llvm.org/libunwind.git' + '@' +
     Var('libunwind_revision'),
+  'win': {
+    'packages': [
+      {
+        'package': 'gn/gn/windows-amd64',
+        'version': Var('gn_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'checkout_win',
+  },
 }
diff --git a/buildtools/linux64/gn.sha1 b/buildtools/linux64/gn.sha1
deleted file mode 100644
index 862f00ac..0000000
--- a/buildtools/linux64/gn.sha1
+++ /dev/null
@@ -1 +0,0 @@
-3523d50538357829725d4ed74b777a572ce0ac74
\ No newline at end of file
diff --git a/buildtools/mac/gn.sha1 b/buildtools/mac/gn.sha1
deleted file mode 100644
index a5b3aaa..0000000
--- a/buildtools/mac/gn.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d43122f6140d0711518aa909980cb009c4fbce3d
\ No newline at end of file
diff --git a/buildtools/third_party/libc++/BUILD.gn b/buildtools/third_party/libc++/BUILD.gn
index 1654038..02a501a 100644
--- a/buildtools/third_party/libc++/BUILD.gn
+++ b/buildtools/third_party/libc++/BUILD.gn
@@ -18,6 +18,17 @@
   }
 }
 
+# Explicitly set version macros to Windows 7 to prevent libc++ from adding a
+# hard dependency on GetSystemTimePreciseAsFileTime, which was introduced in
+# Windows 8.
+config("winver") {
+  defines = [
+    "NTDDI_VERSION=NTDDI_WIN7",
+    "_WIN32_WINNT=_WIN32_WINNT_WIN7",
+    "WINVER=_WIN32_WINNT_WIN7",
+  ]
+}
+
 if (libcxx_is_shared) {
   _libcxx_target_type = "shared_library"
 } else {
@@ -78,6 +89,8 @@
       "trunk/src/support/win32/support.cpp",
       "trunk/src/support/win32/thread_win32.cpp",
     ]
+    configs -= [ "//build/config/win:winver" ]
+    configs += [ ":winver" ]
   }
   configs -= [
     "//build/config/compiler:chromium_code",
diff --git a/buildtools/win/gn.exe.sha1 b/buildtools/win/gn.exe.sha1
deleted file mode 100644
index adb9bc6..0000000
--- a/buildtools/win/gn.exe.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e20768d93a6b4400de0d03bb8ceb46facdbe3883
\ No newline at end of file
diff --git a/chrome/VERSION b/chrome/VERSION
index be99ed47..c173a64 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=75
 MINOR=0
-BUILD=3735
+BUILD=3738
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 0cc38505..cba5172 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1045,6 +1045,24 @@
   variables = [ "manifest_package=$test_manifest_package" ]
 }
 
+config("orderfile_config") {
+  if (chrome_orderfile != "") {
+    _rebased_orderfile = rebase_path(chrome_orderfile, root_build_dir)
+    if (!use_lld) {
+      ldflags = [ "-Wl,-section-ordering-file=$_rebased_orderfile" ]
+    } else {
+      ldflags = [
+        "-Wl,--symbol-ordering-file",
+        "-Wl,$_rebased_orderfile",
+        "-Wl,--no-warn-symbol-ordering",
+      ]
+    }
+    inputs = [
+      chrome_orderfile,
+    ]
+  }
+}
+
 # This template creates a native library for Chrome's APK or bundle.
 template("libchrome_apk_or_bundle_tmpl") {
   chrome_common_shared_library(target_name) {
diff --git a/chrome/android/chrome_common_shared_library.gni b/chrome/android/chrome_common_shared_library.gni
index bf4f5af..efa9d7a 100644
--- a/chrome/android/chrome_common_shared_library.gni
+++ b/chrome/android/chrome_common_shared_library.gni
@@ -16,6 +16,16 @@
 bundle_library_suffix = "_base"
 bundle_pak_asset_type = "bundle"
 
+# This value is set downstream for internal builds.
+if (!defined(default_chrome_orderfile)) {
+  default_chrome_orderfile = ""
+}
+
+declare_args() {
+  # Path to a linker orderfile to use for libchrome.so, libmonochrome.so, etc.
+  chrome_orderfile = default_chrome_orderfile
+}
+
 # This template contains all common configuration for native shared libraries,
 # including libchrome, monochrome, standalone webview (also called monochrome),
 # and libchromefortest (used by chrome_public_test_apk).
@@ -50,6 +60,7 @@
       deps += [ "//chrome:chrome_android_core" ]
     }
 
+    configs += [ "//chrome/android:orderfile_config" ]
     public_configs = extra_chrome_shared_library_configs
     deps += extra_chrome_shared_library_deps
 
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 f67e2a25..f0ee57a 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
@@ -13,6 +13,7 @@
 import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabList;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
@@ -62,15 +63,15 @@
 
     /**
      * Reset the tab grid with the given {@link TabModel}. Can be null.
-     * @param tabModel The current {@link TabModel} to show the tabs for in the grid.
+     * @param tabList The current {@link TabList} to show the tabs for in the grid.
      */
     @Override
-    public void resetWithTabModel(TabModel tabModel) {
+    public void resetWithTabList(TabList tabList) {
         List<Tab> tabs = null;
-        if (tabModel != null) {
+        if (tabList != null) {
             tabs = new ArrayList<>();
-            for (int i = 0; i < tabModel.getCount(); i++) {
-                tabs.add(tabModel.getTabAt(i));
+            for (int i = 0; i < tabList.getCount(); i++) {
+                tabs.add(tabList.getTabAt(i));
             }
         }
         mTabGridCoordinator.resetWithListOfTabs(tabs);
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 ea023180..47538a7 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
@@ -17,11 +17,13 @@
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
+import org.chromium.chrome.browser.tabmodel.TabList;
 import org.chromium.chrome.browser.tabmodel.TabModel;
+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.tabmodel.TabModelSelectorTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -38,7 +40,7 @@
     private final ResetHandler mResetHandler;
     private final PropertyModel mContainerViewModel;
     private final TabModelSelector mTabModelSelector;
-    private final TabModelSelectorTabModelObserver mTabModelObserver;
+    private final TabModelObserver mTabModelObserver;
     private final TabModelSelectorObserver mTabModelSelectorObserver;
     private final ObserverList<OverviewModeObserver> mObservers = new ObserverList<>();
     private final ChromeFullscreenManager mFullscreenManager;
@@ -70,7 +72,7 @@
      * Interface to delegate resetting the tab grid.
      */
     interface ResetHandler {
-        void resetWithTabModel(TabModel tabModel);
+        void resetWithTabList(TabList tabList);
     }
 
     /**
@@ -92,13 +94,16 @@
             @Override
             public void onTabModelSelected(TabModel newModel, TabModel oldModel) {
                 mShouldIgnoreNextSelect = true;
-                mResetHandler.resetWithTabModel(newModel);
-                mContainerViewModel.set(IS_INCOGNITO, newModel.isIncognito());
+
+                TabList currentTabModelFilter =
+                        mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter();
+                mResetHandler.resetWithTabList(currentTabModelFilter);
+                mContainerViewModel.set(IS_INCOGNITO, currentTabModelFilter.isIncognito());
             }
         };
         mTabModelSelector.addObserver(mTabModelSelectorObserver);
 
-        mTabModelObserver = new TabModelSelectorTabModelObserver(mTabModelSelector) {
+        mTabModelObserver = new EmptyTabModelObserver() {
             @Override
             public void didSelectTab(Tab tab, int type, int lastId) {
                 if (type == TabSelectionType.FROM_CLOSE || mShouldIgnoreNextSelect) {
@@ -110,9 +115,13 @@
         };
 
         mFullscreenManager.addListener(mFullscreenListener);
+        mTabModelSelector.getTabModelFilterProvider().addTabModelFilterObserver(mTabModelObserver);
 
         mContainerViewModel.set(VISIBILITY_LISTENER, this);
-        mContainerViewModel.set(IS_INCOGNITO, mTabModelSelector.getCurrentModel().isIncognito());
+        mContainerViewModel.set(IS_INCOGNITO,
+                mTabModelSelector.getTabModelFilterProvider()
+                        .getCurrentTabModelFilter()
+                        .isIncognito());
         mContainerViewModel.set(ANIMATE_VISIBILITY_CHANGES, true);
         mContainerViewModel.set(TOP_CONTROLS_HEIGHT, fullscreenManager.getTopControlsHeight());
         mContainerViewModel.set(
@@ -121,9 +130,12 @@
 
     private void setVisibility(boolean isVisible) {
         if (isVisible) {
-            mResetHandler.resetWithTabModel(mTabModelSelector.getCurrentModel());
+            mResetHandler.resetWithTabList(
+                    mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter());
             int initialPosition = Math.max(
-                    mTabModelSelector.getCurrentModel().index() - INITIAL_SCROLL_INDEX_OFFSET, 0);
+                    mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter().index()
+                            - INITIAL_SCROLL_INDEX_OFFSET,
+                    0);
             mContainerViewModel.set(INITIAL_SCROLL_INDEX, initialPosition);
         }
 
@@ -182,7 +194,7 @@
 
     @Override
     public void finishedHiding() {
-        mResetHandler.resetWithTabModel(null);
+        mResetHandler.resetWithTabList(null);
         mContainerViewModel.set(INITIAL_SCROLL_INDEX, 0);
         for (OverviewModeObserver observer : mObservers) {
             observer.onOverviewModeFinishedHiding();
@@ -195,6 +207,7 @@
     public void destroy() {
         mTabModelSelector.removeObserver(mTabModelSelectorObserver);
         mFullscreenManager.removeListener(mFullscreenListener);
-        mTabModelObserver.destroy();
+        mTabModelSelector.getTabModelFilterProvider().removeTabModelFilterObserver(
+                mTabModelObserver);
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
index eaeda45..e5e2d36 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
@@ -80,7 +80,8 @@
 
             @Override
             public void didAddTab(Tab tab, int type) {
-                if (type == TabLaunchType.FROM_CHROME_UI) return;
+                if (type == TabLaunchType.FROM_CHROME_UI || type == TabLaunchType.FROM_RESTORE)
+                    return;
                 resetTabStripWithRelatedTabsForId(tab.getId());
             }
 
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 a2db1dfe..0593c718 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
@@ -20,6 +20,8 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tasks.tab_groups.TabGroupUtils;
+import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.RecyclerViewAdapter;
@@ -46,20 +48,22 @@
     private final SimpleRecyclerViewMcpBase mModelChangeProcessor;
     private final TabListMediator mMediator;
     private final TabListRecyclerView mRecyclerView;
+    private final @TabListMode int mMode;
 
     TabListCoordinator(@TabListMode int mode, Context context, TabModelSelector tabModelSelector,
             TabContentManager tabContentManager, @NonNull ViewGroup parentView,
             boolean attachToParent) {
         TabListModel tabListModel = new TabListModel();
+        mMode = mode;
 
         RecyclerViewAdapter adapter;
-        if (mode == TabListMode.GRID) {
+        if (mMode == TabListMode.GRID) {
             SimpleRecyclerViewMcpBase<PropertyModel, TabGridViewHolder, PropertyKey> mcp =
                     new SimpleRecyclerViewMcpBase<>(
                             null, TabGridViewBinder::onBindViewHolder, tabListModel);
             adapter = new RecyclerViewAdapter<>(mcp, TabGridViewHolder::create);
             mModelChangeProcessor = mcp;
-        } else if (mode == TabListMode.STRIP) {
+        } else if (mMode == TabListMode.STRIP) {
             SimpleRecyclerViewMcpBase<PropertyModel, TabStripViewHolder, PropertyKey> mcp =
                     new SimpleRecyclerViewMcpBase<>(
                             null, TabStripViewBinder::onBindViewHolder, tabListModel);
@@ -81,9 +85,9 @@
 
         mRecyclerView.setAdapter(adapter);
 
-        if (mode == TabListMode.GRID) {
+        if (mMode == TabListMode.GRID) {
             mRecyclerView.setLayoutManager(new GridLayoutManager(context, GRID_LAYOUT_SPAN_COUNT));
-        } else if (mode == TabListMode.STRIP) {
+        } else if (mMode == TabListMode.STRIP) {
             mRecyclerView.setLayoutManager(
                     new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
         }
@@ -108,6 +112,11 @@
      */
     public void resetWithListOfTabs(@Nullable List<Tab> tabs) {
         mMediator.resetWithListOfTabs(tabs);
+
+        if (mMode == TabListMode.STRIP && tabs != null && tabs.size() > 1) {
+            TabGroupUtils.maybeShowIPH(
+                    FeatureConstants.TAB_GROUPS_TAP_TO_SEE_ANOTHER_TAB_FEATURE, mRecyclerView);
+        }
     }
 
     /**
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 63717299..d24a540 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
@@ -41,6 +41,8 @@
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider;
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
@@ -79,6 +81,10 @@
     @Mock
     TabModel mTabModel;
     @Mock
+    TabModelFilter mTabModelFilter;
+    @Mock
+    TabModelFilterProvider mTabModelFilterProvider;
+    @Mock
     Context mContext;
     @Mock
     Resources mResources;
@@ -124,14 +130,18 @@
         doReturn(mTabModel).when(mTabModelSelector).getCurrentModel();
         doReturn(tabModelList).when(mTabModelSelector).getModels();
         doNothing().when(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
+        doReturn(mTabModelFilterProvider).when(mTabModelSelector).getTabModelFilterProvider();
+        doReturn(mTabModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
 
-        doNothing().when(mTabModel).addObserver(mTabModelObserverCaptor.capture());
-        doReturn(mTab1).when(mTabModel).getTabAt(0);
-        doReturn(mTab2).when(mTabModel).getTabAt(1);
-        doReturn(mTab2).when(mTabModel).getTabAt(2);
-        doReturn(false).when(mTabModel).isIncognito();
-        doReturn(2).when(mTabModel).index();
-        doReturn(3).when(mTabModel).getCount();
+        doNothing()
+                .when(mTabModelFilterProvider)
+                .addTabModelFilterObserver(mTabModelObserverCaptor.capture());
+        doReturn(mTab1).when(mTabModelFilter).getTabAt(0);
+        doReturn(mTab2).when(mTabModelFilter).getTabAt(1);
+        doReturn(mTab2).when(mTabModelFilter).getTabAt(2);
+        doReturn(false).when(mTabModelFilter).isIncognito();
+        doReturn(2).when(mTabModelFilter).index();
+        doReturn(3).when(mTabModelFilter).getCount();
 
         doReturn(CONTROL_HEIGHT_DEFAULT).when(mFullscreenManager).getBottomControlsHeight();
         doReturn(CONTROL_HEIGHT_DEFAULT).when(mFullscreenManager).getTopControlsHeight();
@@ -160,7 +170,7 @@
         initAndAssertAllProperties();
         mMediator.showOverview(true);
 
-        verify(mResetHandler).resetWithTabModel(mTabModel);
+        verify(mResetHandler).resetWithTabList(mTabModelFilter);
 
         assertThat(
                 mModel.get(TabListContainerProperties.ANIMATE_VISIBILITY_CHANGES), equalTo(true));
@@ -181,7 +191,7 @@
         inOrder.verify(mPropertyObserver)
                 .onPropertyChanged(mModel, TabListContainerProperties.ANIMATE_VISIBILITY_CHANGES);
 
-        verify(mResetHandler).resetWithTabModel(mTabModel);
+        verify(mResetHandler).resetWithTabList(mTabModelFilter);
 
         assertThat(
                 mModel.get(TabListContainerProperties.ANIMATE_VISIBILITY_CHANGES), equalTo(true));
@@ -263,7 +273,7 @@
     public void resetsToNullAfterHidingFinishes() {
         initAndAssertAllProperties();
         mMediator.finishedHiding();
-        verify(mResetHandler).resetWithTabModel(eq(null));
+        verify(mResetHandler).resetWithTabList(eq(null));
         assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(0));
     }
 
@@ -271,9 +281,9 @@
     public void resetsAfterNewTabModelSelected() {
         initAndAssertAllProperties();
 
-        doReturn(true).when(mTabModel).isIncognito();
+        doReturn(true).when(mTabModelFilter).isIncognito();
         mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(mTabModel, null);
-        verify(mResetHandler).resetWithTabModel(eq(mTabModel));
+        verify(mResetHandler).resetWithTabList(eq(mTabModelFilter));
         assertThat(mModel.get(TabListContainerProperties.IS_INCOGNITO), equalTo(true));
 
         // Switching TabModels by itself shouldn't cause visibility changes.
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml
index 0914c95a..b1d76956 100644
--- a/chrome/android/java/res/values/colors.xml
+++ b/chrome/android/java/res/values/colors.xml
@@ -102,7 +102,7 @@
     <color name="data_reduction_chart_background_color">@color/modern_grey_50</color>
 
     <!-- Compositor Tab Title Colors -->
-    <color name="compositor_tab_title_bar_text">@color/modern_grey_900</color>
+    <color name="compositor_tab_title_bar_text">@color/default_text_color</color>
     <color name="compositor_tab_title_bar_text_incognito">@android:color/white</color>
 
     <!-- Account Signin Colors -->
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 7df2d90..8ba83bf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -732,7 +732,8 @@
                 UsageStatsService.getInstance().createPageViewObserver(mTabModelSelectorImpl, this);
             }
 
-            if (FeatureUtilities.isGridTabSwitcherEnabled(this)) {
+            if (FeatureUtilities.isGridTabSwitcherEnabled(this)
+                    || FeatureUtilities.isTabGroupsAndroidEnabled()) {
                 GridTabSwitcher gridTabSwitcher =
                         TabManagementModuleProvider.getTabManagementModule().createGridTabSwitcher(
                                 this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/NavigationBarColorController.java b/chrome/android/java/src/org/chromium/chrome/browser/NavigationBarColorController.java
index 93d9cd5..4591309a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/NavigationBarColorController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/NavigationBarColorController.java
@@ -9,6 +9,7 @@
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.os.Build;
+import android.support.annotation.Nullable;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
@@ -35,10 +36,12 @@
     private final Window mWindow;
     private final ViewGroup mRootView;
     private final Resources mResources;
-    private final TabModelSelector mTabModelSelector;
-    private final TabModelSelectorObserver mTabModelSelectorObserver;
-    private final OverviewModeBehavior mOverviewModeBehavior;
-    private final OverviewModeObserver mOverviewModeObserver;
+
+    // May be null if we return from the constructor early. Otherwise will be set.
+    private final @Nullable TabModelSelector mTabModelSelector;
+    private final @Nullable TabModelSelectorObserver mTabModelSelectorObserver;
+    private final @Nullable OverviewModeBehavior mOverviewModeBehavior;
+    private final @Nullable OverviewModeObserver mOverviewModeObserver;
 
     private boolean mUseLightNavigation;
     private boolean mOverviewModeHiding;
@@ -58,6 +61,17 @@
         mWindow = window;
         mRootView = (ViewGroup) mWindow.getDecorView().getRootView();
         mResources = mRootView.getResources();
+
+        // If we're not using a light navigation bar, it will always be black so there's no need
+        // to register observers and manipulate coloring.
+        if (!mResources.getBoolean(R.bool.window_light_navigation_bar)) {
+            mTabModelSelector = null;
+            mTabModelSelectorObserver = null;
+            mOverviewModeBehavior = null;
+            mOverviewModeObserver = null;
+            return;
+        }
+
         mUseLightNavigation = true;
 
         mTabModelSelector = tabModelSelector;
@@ -102,8 +116,10 @@
      * Destroy this {@link NavigationBarColorController} instance.
      */
     public void destroy() {
-        mTabModelSelector.removeObserver(mTabModelSelectorObserver);
-        mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
+        if (mTabModelSelector != null) mTabModelSelector.removeObserver(mTabModelSelectorObserver);
+        if (mOverviewModeBehavior != null) {
+            mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
+        }
         VrModuleProvider.unregisterVrModeObserver(this);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java
index 739fa2e..01ec675 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java
@@ -81,7 +81,7 @@
     public int getItemPosition(@NonNull Object object) {
         ViewGroup viewToBeFound = (ViewGroup) object;
         for (int i = 0; i < mTabList.size(); i++) {
-            if (mViews.get(mTabList.get(i)).equals(viewToBeFound)) {
+            if (viewToBeFound.equals(mViews.get(mTabList.get(i)))) {
                 return i; // The tab the view is connected to still exists and its position is i.
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/CachedProviderAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/CachedProviderAdapter.java
index 17be583..aaaa2e6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/CachedProviderAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/CachedProviderAdapter.java
@@ -7,7 +7,6 @@
 import android.support.annotation.Nullable;
 
 import org.chromium.base.Callback;
-import org.chromium.chrome.browser.tab.Tab;
 
 /**
  * Provides a cache for a given provider. New sets of data will only be cached and not
@@ -17,7 +16,6 @@
  */
 class CachedProviderAdapter<T> extends PropertyProvider<T> implements Provider.Observer<T> {
     private final @Nullable Callback<CachedProviderAdapter> mNewCachedDataAvailable;
-    private final Tab mTab;
     private T mLastItems;
 
     /**
@@ -29,10 +27,9 @@
      * @param tab A {@link Tab}
      */
     CachedProviderAdapter(PropertyProvider<T> provider, T defaultItems,
-            @Nullable Callback<CachedProviderAdapter> newCachedDataAvailable, Tab tab) {
+            @Nullable Callback<CachedProviderAdapter> newCachedDataAvailable) {
         super(provider.mType);
         mNewCachedDataAvailable = newCachedDataAvailable;
-        mTab = tab;
         provider.addObserver(this);
         mLastItems = defaultItems;
     }
@@ -46,10 +43,6 @@
         notifyObservers(mLastItems);
     }
 
-    Tab getTab() {
-        return mTab;
-    }
-
     @Override
     public void onItemAvailable(int typeId, T actions) {
         mLastItems = actions;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
index a720b44..30c4db3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
@@ -99,7 +99,7 @@
     }
 
     void registerPasswordProvider(
-            Provider<KeyboardAccessoryData.AccessorySheetData> sheetDataProvider) {
+            PropertyProvider<KeyboardAccessoryData.AccessorySheetData> sheetDataProvider) {
         mMediator.registerPasswordProvider(sheetDataProvider);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
index 3c4d33d..8f2e107 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
@@ -29,13 +29,11 @@
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
-import org.chromium.chrome.browser.tabmodel.TabSelectionType;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.DropdownPopupWindow;
 import org.chromium.ui.base.WindowAndroid;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.util.HashSet;
 
 /**
  * This part of the manual filling component manages the state of the manual filling flow depending
@@ -51,28 +49,12 @@
     private boolean mShouldShow;
     private final KeyboardExtensionSizeManager mKeyboardExtensionSizeManager =
             new KeyboardExtensionSizeManager();
-
-    /**
-     * This class holds all data that is necessary to restore the state of the Keyboard accessory
-     * and its sheet for a given tab.
-     */
-    @VisibleForTesting
-    static class AccessoryState {
-        @Nullable
-        CachedProviderAdapter<Action[]> mActionsProvider;
-        @Nullable
-        PasswordAccessorySheetCoordinator mPasswordAccessorySheet;
-        @Nullable
-        CreditCardAccessorySheetCoordinator mCreditCardAccessorySheet;
-    }
-
-    // TODO(fhorschig): Do we need a MapObservable type? (This would be only observer though).
-    private final Map<Tab, AccessoryState> mModel = new HashMap<>();
+    private final ManualFillingStateCache mStateCache = new ManualFillingStateCache();
+    private final HashSet<Tab> mObservedTabs = new HashSet<>();
     private KeyboardAccessoryCoordinator mKeyboardAccessory;
     private AccessorySheetCoordinator mAccessorySheet;
     private ChromeActivity mActivity; // Used to control the keyboard.
     private TabModelSelectorTabModelObserver mTabModelObserver;
-    private Tab mActiveBrowserTab;
     private DropdownPopupWindow mPopup;
 
     private final SceneChangeObserver mTabSwitcherObserver = new SceneChangeObserver() {
@@ -97,9 +79,9 @@
 
         @Override
         public void onDestroyed(Tab tab) {
-            mModel.remove(tab); // Clears tab if still present.
-            if (tab == mActiveBrowserTab) mActiveBrowserTab = null;
-            restoreCachedState(mActiveBrowserTab);
+            mStateCache.destroyStateFor(tab);
+            pause();
+            refreshTabs();
         }
 
         @Override
@@ -122,27 +104,19 @@
         mActivity.findViewById(android.R.id.content).addOnLayoutChangeListener(this);
         mTabModelObserver = new TabModelSelectorTabModelObserver(mActivity.getTabModelSelector()) {
             @Override
-            public void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) {
-                mActiveBrowserTab = tab;
-                restoreCachedState(tab);
+            public void didSelectTab(Tab tab, int type, int lastId) {
+                ensureObserverRegistered(tab);
+                refreshTabs();
             }
 
             @Override
             public void tabClosureCommitted(Tab tab) {
-                mModel.remove(tab);
-            }
-
-            @Override
-            public void willCloseTab(Tab tab, boolean animate) {
-                if (mActiveBrowserTab == tab) mActiveBrowserTab = null;
-                restoreCachedState(mActiveBrowserTab);
+                super.tabClosureCommitted(tab);
+                mStateCache.destroyStateFor(tab);
             }
         };
-        Tab currentTab = mActivity.getTabModelSelector().getCurrentTab();
-        if (currentTab != null) {
-            mTabModelObserver.didSelectTab(
-                    currentTab, TabSelectionType.FROM_USER, Tab.INVALID_TAB_ID);
-        }
+        ensureObserverRegistered(getActiveBrowserTab());
+        refreshTabs();
     }
 
     boolean isInitialized() {
@@ -181,29 +155,27 @@
         }
     }
 
-    void registerPasswordProvider(Provider<KeyboardAccessoryData.AccessorySheetData> dataProvider) {
-        PasswordAccessorySheetCoordinator accessorySheet = getPasswordAccessorySheet();
+    void registerPasswordProvider(
+            PropertyProvider<KeyboardAccessoryData.AccessorySheetData> dataProvider) {
+        ManualFillingState state = mStateCache.getStateFor(mActivity.getCurrentWebContents());
+
+        state.wrapPasswordSheetDataProvider(dataProvider);
+        PasswordAccessorySheetCoordinator accessorySheet = getOrCreatePasswordSheet();
         if (accessorySheet == null) return; // Not available or initialized yet.
-        accessorySheet.registerDataProvider(dataProvider);
+        accessorySheet.registerDataProvider(state.getPasswordSheetDataProvider());
     }
 
     void registerCreditCardProvider() {
-        CreditCardAccessorySheetCoordinator accessorySheet = getCreditCardAccessorySheet();
+        CreditCardAccessorySheetCoordinator accessorySheet = getOrCreateCreditCardSheet();
         if (accessorySheet == null) return;
     }
 
     void registerActionProvider(PropertyProvider<Action[]> actionProvider) {
         if (!isInitialized()) return;
-        if (mActiveBrowserTab == null) return;
-        CachedProviderAdapter<Action[]> adapter = new CachedProviderAdapter<>(
-                actionProvider, new Action[0], this::onCacheReceivedNewData, mActiveBrowserTab);
-        mModel.get(mActiveBrowserTab).mActionsProvider = adapter;
-        mKeyboardAccessory.registerActionProvider(adapter);
-    }
+        ManualFillingState state = mStateCache.getStateFor(mActivity.getCurrentWebContents());
 
-    private void onCacheReceivedNewData(CachedProviderAdapter cachedProviderAdapter) {
-        if (mActiveBrowserTab != cachedProviderAdapter.getTab()) return;
-        cachedProviderAdapter.notifyAboutCachedItems();
+        state.wrapActionsProvider(actionProvider, new Action[0]);
+        mKeyboardAccessory.registerActionProvider(state.getActionsProvider());
     }
 
     void destroy() {
@@ -211,6 +183,9 @@
         pause();
         mActivity.findViewById(android.R.id.content).removeOnLayoutChangeListener(this);
         mTabModelObserver.destroy();
+        mStateCache.destroy();
+        for (Tab tab : mObservedTabs) tab.removeObserver(mTabObserver);
+        mObservedTabs.clear();
         LayoutManager manager = getLayoutManager();
         if (manager != null) manager.removeSceneChangeObserver(mTabSwitcherObserver);
         mWindowAndroid = null;
@@ -260,7 +235,8 @@
 
     void resume() {
         if (!isInitialized()) return;
-        restoreCachedState(mActiveBrowserTab);
+        pause(); // Resuming dismisses the keyboard. Ensure the accessory doesn't linger.
+        refreshTabs();
     }
 
     private void displayKeyboardAccessory() {
@@ -279,8 +255,8 @@
     }
 
     private boolean hasSufficientSpace() {
-        if (mActivity == null || mActiveBrowserTab == null) return false;
-        WebContents webContents = mActiveBrowserTab.getWebContents();
+        if (mActivity == null) return false;
+        WebContents webContents = mActivity.getCurrentWebContents();
         if (webContents == null) return false;
         float height = webContents.getHeight(); // getHeight actually returns dip, not Px!
         height += calculateAccessoryBarHeight() / mWindowAndroid.getDisplay().getDipScale();
@@ -360,7 +336,7 @@
      */
     private @Nullable ViewGroup getContentView() {
         if (mActivity == null) return null;
-        Tab tab = mActivity.getActivityTab();
+        Tab tab = getActiveBrowserTab();
         if (tab == null) return null;
         return tab.getContentView();
     }
@@ -377,41 +353,32 @@
         return compositorViewHolder.getLayoutManager();
     }
 
+    /**
+     * Shorthand to get the activity tab.
+     * @return The currently visible {@link Tab}, if any.
+     */
+    private @Nullable Tab getActiveBrowserTab() {
+        return mActivity.getActivityTabProvider().getActivityTab();
+    }
+
+    /**
+     * Registers a {@link TabObserver} to the given {@link Tab} if it hasn't been done yet.
+     * Using this function avoid deleting and readding the observer (each O(N)) since the tab does
+     * not report whether an observer is registered.
+     * @param tab A {@link Tab}. May be the currently active tab which is allowed to be null.
+     */
+    private void ensureObserverRegistered(@Nullable Tab tab) {
+        if (tab == null) return; // No tab given, no observer necessary.
+        if (!mObservedTabs.add(tab)) return; // Observer already registered.
+        tab.addObserver(mTabObserver);
+    }
+
     private ChromeKeyboardVisibilityDelegate getKeyboard() {
         assert mWindowAndroid instanceof ChromeWindow;
         assert mWindowAndroid.getKeyboardDelegate() instanceof ChromeKeyboardVisibilityDelegate;
         return (ChromeKeyboardVisibilityDelegate) mWindowAndroid.getKeyboardDelegate();
     }
 
-    private AccessoryState getOrCreateAccessoryState(Tab tab) {
-        assert tab != null : "Accessory state was requested without providing a non-null tab!";
-        AccessoryState state = mModel.get(tab);
-        if (state != null) return state;
-        state = new AccessoryState();
-        mModel.put(tab, state);
-        tab.addObserver(mTabObserver);
-        return state;
-    }
-
-    private void restoreCachedState(Tab browserTab) {
-        pause();
-        clearTabs();
-        if (browserTab == null) return; // If there is no tab, exit after cleaning everything.
-        AccessoryState state = getOrCreateAccessoryState(browserTab);
-        if (state.mPasswordAccessorySheet != null) {
-            addTab(state.mPasswordAccessorySheet.getTab());
-        }
-        if (state.mCreditCardAccessorySheet != null) {
-            addTab(state.mCreditCardAccessorySheet.getTab());
-        }
-        if (state.mActionsProvider != null) state.mActionsProvider.notifyAboutCachedItems();
-    }
-
-    private void clearTabs() {
-        mKeyboardAccessory.setTabs(new KeyboardAccessoryData.Tab[0]);
-        mAccessorySheet.setTabs(new KeyboardAccessoryData.Tab[0]);
-    }
-
     private @Px int calculateAccessorySheetHeight(View rootView) {
         InsetObserverView insetObserver = mInsetObserverViewSupplier.get();
         if (insetObserver != null) return insetObserver.getSystemWindowInsetsBottom();
@@ -427,36 +394,48 @@
                 org.chromium.chrome.R.dimen.keyboard_accessory_suggestion_height);
     }
 
-    @VisibleForTesting
-    void addTab(KeyboardAccessoryData.Tab tab) {
+    private void refreshTabs() {
         if (!isInitialized()) return;
-        // TODO(fhorschig): This should add the tab only to the state. Sheet and accessory should be
-        // using a |set| method or even observe the state.
-        mKeyboardAccessory.addTab(tab);
-        mAccessorySheet.addTab(tab);
+        KeyboardAccessoryData.Tab[] tabs =
+                mStateCache.getStateFor(mActivity.getCurrentWebContents()).getTabs();
+        mKeyboardAccessory.setTabs(tabs);
+        mAccessorySheet.setTabs(tabs);
     }
 
+    /**
+     * Returns the password sheet for the current WebContents or creates one if it doesn't exist.
+     * @return A {@link PasswordAccessorySheetCoordinator} or null if unavailable.
+     */
     @VisibleForTesting
     @Nullable
-    PasswordAccessorySheetCoordinator getPasswordAccessorySheet() {
+    PasswordAccessorySheetCoordinator getOrCreatePasswordSheet() {
         if (!isInitialized()) return null;
         if (!ChromeFeatureList.isEnabled(ChromeFeatureList.EXPERIMENTAL_UI)
                 && !ChromeFeatureList.isEnabled(ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY)) {
             return null;
         }
-        if (mActiveBrowserTab == null) return null; // No need for a sheet if there is no tab.
-        AccessoryState state = getOrCreateAccessoryState(mActiveBrowserTab);
-        if (state.mPasswordAccessorySheet == null) {
-            state.mPasswordAccessorySheet = new PasswordAccessorySheetCoordinator(
-                    mActivity, mAccessorySheet.getScrollListener());
-            addTab(state.mPasswordAccessorySheet.getTab());
+        WebContents webContents = mActivity.getCurrentWebContents();
+        if (webContents == null) return null; // There is no active tab or it's being destroyed.
+        ManualFillingState state = mStateCache.getStateFor(webContents);
+        if (state.getPasswordAccessorySheet() != null) return state.getPasswordAccessorySheet();
+
+        PasswordAccessorySheetCoordinator passwordSheet = new PasswordAccessorySheetCoordinator(
+                mActivity, mAccessorySheet.getScrollListener());
+        state.setPasswordAccessorySheet(passwordSheet);
+        if (state.getPasswordSheetDataProvider() != null) {
+            passwordSheet.registerDataProvider(state.getPasswordSheetDataProvider());
         }
-        return state.mPasswordAccessorySheet;
+        refreshTabs();
+        return passwordSheet;
     }
 
+    /**
+     * Returns the credit card sheet for the current WebContents or creates one if it doesn't exist.
+     * @return A {@link CreditCardAccessorySheetCoordinator} or null if unavailable.
+     */
     @VisibleForTesting
     @Nullable
-    CreditCardAccessorySheetCoordinator getCreditCardAccessorySheet() {
+    CreditCardAccessorySheetCoordinator getOrCreateCreditCardSheet() {
         if (!isInitialized()) return null;
         if (!ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID)) {
             return null;
@@ -465,14 +444,14 @@
                 && !ChromeFeatureList.isEnabled(ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY)) {
             return null;
         }
-        if (mActiveBrowserTab == null) return null; // No need for a sheet if there is no tab.
-        AccessoryState state = getOrCreateAccessoryState(mActiveBrowserTab);
-        if (state.mCreditCardAccessorySheet == null) {
-            state.mCreditCardAccessorySheet = new CreditCardAccessorySheetCoordinator(
-                    mActivity, mAccessorySheet.getScrollListener());
-            addTab(state.mCreditCardAccessorySheet.getTab());
-        }
-        return state.mCreditCardAccessorySheet;
+        WebContents webContents = mActivity.getCurrentWebContents();
+        if (webContents == null) return null; // There is no active tab or it's being destroyed.
+        ManualFillingState state = mStateCache.getStateFor(webContents);
+        if (state.getCreditCardAccessorySheet() != null) return state.getCreditCardAccessorySheet();
+        state.setCreditCardAccessorySheet(new CreditCardAccessorySheetCoordinator(
+                mActivity, mAccessorySheet.getScrollListener()));
+        refreshTabs();
+        return state.getCreditCardAccessorySheet();
     }
 
     @VisibleForTesting
@@ -502,7 +481,7 @@
     }
 
     @VisibleForTesting
-    Map<Tab, AccessoryState> getModelForTesting() {
-        return mModel;
+    ManualFillingStateCache getStateCacheForTesting() {
+        return mStateCache;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingState.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingState.java
new file mode 100644
index 0000000..d1e5c78
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingState.java
@@ -0,0 +1,132 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill.keyboard_accessory;
+
+import android.support.annotation.Nullable;
+
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.AccessorySheetData;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_public.browser.WebContentsObserver;
+
+import java.util.ArrayList;
+
+/**
+ * This class holds all data that is necessary to restore the state of the Keyboard accessory
+ * and its sheet for the {@link WebContents} it is attached to.
+ */
+class ManualFillingState {
+    private final WebContents mWebContents;
+    private boolean mWebContentsShowing;
+    private @Nullable CachedProviderAdapter<KeyboardAccessoryData.Action[]> mActionsProvider;
+    private @Nullable CachedProviderAdapter<AccessorySheetData> mPasswordSheetDataProvider;
+    private @Nullable PasswordAccessorySheetCoordinator mPasswordAccessorySheet;
+    private @Nullable CreditCardAccessorySheetCoordinator mCreditCardAccessorySheet;
+
+    private final WebContentsObserver mWebContentsObserver = new WebContentsObserver() {
+        @Override
+        public void wasShown() {
+            super.wasShown();
+            mWebContentsShowing = true;
+            if (mActionsProvider != null) mActionsProvider.notifyAboutCachedItems();
+            if (mPasswordSheetDataProvider != null)
+                mPasswordSheetDataProvider.notifyAboutCachedItems();
+        }
+
+        @Override
+        public void wasHidden() {
+            super.wasHidden();
+            mWebContentsShowing = false;
+        }
+    };
+
+    /**
+     * Creates a new set of user data that is bound to and observing the given web contents.
+     * @param webContents Some {@link WebContents} which are assumed to be shown right now.
+     */
+    ManualFillingState(@Nullable WebContents webContents) {
+        mWebContents = webContents;
+        if (webContents == null) return;
+        mWebContentsShowing = true;
+        mWebContents.addObserver(mWebContentsObserver);
+    }
+
+    public KeyboardAccessoryData.Tab[] getTabs() {
+        ArrayList<KeyboardAccessoryData.Tab> tabs = new ArrayList<>();
+        if (mPasswordAccessorySheet != null) tabs.add(mPasswordAccessorySheet.getTab());
+        if (mCreditCardAccessorySheet != null) tabs.add(mCreditCardAccessorySheet.getTab());
+        return tabs.toArray(new KeyboardAccessoryData.Tab[0]);
+    }
+
+    public void destroy() {
+        if (mWebContents != null) mWebContents.removeObserver(mWebContentsObserver);
+        mActionsProvider = null;
+        mPasswordSheetDataProvider = null;
+        mPasswordAccessorySheet = null;
+        mCreditCardAccessorySheet = null;
+        mWebContentsShowing = false;
+    }
+
+    /**
+     * Wraps the given ActionProvider in a {@link CachedProviderAdapter} and stores it.
+     * @param provider A {@link PropertyProvider} providing actions.
+     * @param defaultActions A default set of actions to prepopulate the adapter's cache.
+     */
+    public void wrapActionsProvider(PropertyProvider<KeyboardAccessoryData.Action[]> provider,
+            KeyboardAccessoryData.Action[] defaultActions) {
+        mActionsProvider = new CachedProviderAdapter<>(
+                provider, defaultActions, this::onAdapterReceivedNewData);
+    }
+
+    /**
+     * Returns the wrapped provider set with {@link #wrapActionsProvider}.
+     * @return A {@link CachedProviderAdapter} wrapping a {@link PropertyProvider}.
+     */
+    public Provider<KeyboardAccessoryData.Action[]> getActionsProvider() {
+        return mActionsProvider;
+    }
+
+    /**
+     * Wraps the given provider for password data in a {@link CachedProviderAdapter} and stores it.
+     * @param provider A {@link PropertyProvider} providing password sheet data.
+     */
+    public void wrapPasswordSheetDataProvider(PropertyProvider<AccessorySheetData> provider) {
+        mPasswordSheetDataProvider =
+                new CachedProviderAdapter<>(provider, null, this::onAdapterReceivedNewData);
+    }
+
+    /**
+     * Returns the wrapped provider set with {@link #wrapPasswordSheetDataProvider}.
+     * @return A {@link CachedProviderAdapter} wrapping a {@link PropertyProvider}.
+     */
+    public Provider<AccessorySheetData> getPasswordSheetDataProvider() {
+        return mPasswordSheetDataProvider;
+    }
+
+    public void setPasswordAccessorySheet(@Nullable PasswordAccessorySheetCoordinator sheet) {
+        mPasswordAccessorySheet = sheet;
+    }
+
+    public @Nullable PasswordAccessorySheetCoordinator getPasswordAccessorySheet() {
+        return mPasswordAccessorySheet;
+    }
+
+    public void setCreditCardAccessorySheet(@Nullable CreditCardAccessorySheetCoordinator sheet) {
+        mCreditCardAccessorySheet = sheet;
+    }
+
+    public @Nullable CreditCardAccessorySheetCoordinator getCreditCardAccessorySheet() {
+        return mCreditCardAccessorySheet;
+    }
+
+    private void onAdapterReceivedNewData(CachedProviderAdapter adapter) {
+        if (mWebContentsShowing) adapter.notifyAboutCachedItems();
+    }
+
+    @VisibleForTesting
+    WebContentsObserver getWebContentsObserverForTesting() {
+        return mWebContentsObserver;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingStateCache.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingStateCache.java
new file mode 100644
index 0000000..5b18fd4
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingStateCache.java
@@ -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.
+
+package org.chromium.chrome.browser.autofill.keyboard_accessory;
+
+import android.support.annotation.Nullable;
+
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.content_public.browser.WebContents;
+
+import java.util.HashMap;
+
+/**
+ * This class stores the state of the ManualFilling-components. It holds {@link ManualFillingState}s
+ * associated to {@link WebContents} until explicitly destroyed.
+ *
+ * Example use:
+ * <code>
+ *    ManualFillingStateCache cache = new ManualFillingCache();
+ *    @NonNull WebContents w1 = [...];
+ *    @NonNull WebContents w2 = [...];
+ *    assert cache.getStateFor(w1) == cache.getStateFor(w1);
+ *    assert cache.getStateFor(w1) != cache.getStateFor(w2);
+ *    assert cache.getStateFor(null) != cache.getStateFor(null);
+ *    cache.destroyStateFor(w1); // State for w1 cleaned; reference to w2 dropped.
+ *    caches.destroy(); // State for w2 cleaned; reference to w2 dropped.
+ * </code>
+ */
+class ManualFillingStateCache {
+    private final HashMap<WebContents, ManualFillingState> mStatesForWebContents = new HashMap<>();
+
+    ManualFillingStateCache() {}
+
+    /**
+     * @see #getStateFor(WebContents)
+     * @param tab A {@link Tab} for whose {@link WebContents} a state is needed.
+     * @return A {@link ManualFillingState}. Never null.
+     */
+    public ManualFillingState getStateFor(Tab tab) {
+        return getStateFor(tab.getWebContents());
+    }
+
+    /**
+     * Returns a state for the given WebContents and caches it. If the given WebContents are null,
+     * the returned empty state is not cached.
+     * @param webContents {@link WebContents} for which a state is needed.
+     * @return A {@link ManualFillingState}. Never null.
+     */
+    public ManualFillingState getStateFor(@Nullable WebContents webContents) {
+        if (webContents == null) {
+            // If state is requested for destroyed or invalid WebContents, it returns a null object.
+            return new ManualFillingState(null);
+        }
+        ManualFillingState state = mStatesForWebContents.get(webContents);
+        if (state != null) return state;
+        state = new ManualFillingState(webContents);
+        mStatesForWebContents.put(webContents, state);
+        return state;
+    }
+
+    /**
+     * Destroys all held states and removes the held references to the WebContents they belong to.
+     */
+    public void destroy() {
+        for (ManualFillingState userState : mStatesForWebContents.values()) userState.destroy();
+        mStatesForWebContents.clear();
+    }
+
+    /**
+     * @see #destroyStateFor(WebContents)
+     * @param tab The tab whose WebContents are going to be destroyed.
+     */
+    public void destroyStateFor(Tab tab) {
+        destroyStateFor(tab.getWebContents());
+    }
+
+    /**
+     * Ensures a reference to WebContents isn't held longer than necessary so GC can collect it.
+     * @param webContents The WebContents about to be destroyed and should not be held any longer.
+     */
+    public void destroyStateFor(WebContents webContents) {
+        if (webContents != null) {
+            getStateFor(webContents).destroy();
+            mStatesForWebContents.remove(webContents);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
index 35ce0e0..c0a541d5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
@@ -365,7 +365,7 @@
             // dialog to the user.
             System.exit(-1);
         }
-        ChildProcessLauncherHelper.warmUp(context);
+        ChildProcessLauncherHelper.warmUp(context, true);
     }
 
     public boolean warmup(long flags) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java
index 61f8055..8fec0dda 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java
@@ -11,11 +11,14 @@
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.library_loader.ProcessInitException;
-import org.chromium.base.task.AsyncTask;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.browser.ChromeActivitySessionTracker;
 import org.chromium.chrome.browser.ChromeVersionInfo;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.components.variations.firstrun.VariationsSeedFetcher;
 import org.chromium.content_public.browser.ChildProcessLauncherHelper;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
 
 import java.util.concurrent.Executor;
 
@@ -37,10 +40,11 @@
         return ChromeVersionInfo.isOfficialBuild();
     }
 
-    private class FetchSeedTask extends AsyncTask<Void> {
+    private class FetchSeedTask implements Runnable {
         private final String mRestrictMode;
         private final String mMilestone;
         private final String mChannel;
+        private boolean mShouldRun = true;
 
         public FetchSeedTask(String restrictMode) {
             mRestrictMode = restrictMode;
@@ -49,15 +53,24 @@
         }
 
         @Override
-        protected Void doInBackground() {
+        public void run() {
             VariationsSeedFetcher.get().fetchSeed(mRestrictMode, mMilestone, mChannel);
-            return null;
+            PostTask.postTask(UiThreadTaskTraits.BOOTSTRAP, new Runnable() {
+                @Override
+                public void run() {
+                    if (!shouldRun()) return;
+                    mFetchingVariations = false;
+                    tasksPossiblyComplete(true);
+                }
+            });
         }
 
-        @Override
-        protected void onPostExecute(Void result) {
-            mFetchingVariations = false;
-            tasksPossiblyComplete(true);
+        public synchronized void cancel() {
+            mShouldRun = false;
+        }
+
+        private synchronized boolean shouldRun() {
+            return mShouldRun;
         }
 
         private String getChannelString() {
@@ -98,7 +111,7 @@
                 @Override
                 public void onResult(String restrictMode) {
                     mFetchSeedTask = new FetchSeedTask(restrictMode);
-                    mFetchSeedTask.executeOnExecutor(getFetchSeedExecutor());
+                    PostTask.postTask(TaskTraits.USER_BLOCKING, mFetchSeedTask);
                 }
             });
         }
@@ -149,24 +162,22 @@
         ThreadUtils.assertOnUiThread();
 
         if (!result) {
-            if (mFetchSeedTask != null) mFetchSeedTask.cancel(true);
+            if (mFetchSeedTask != null) mFetchSeedTask.cancel();
             onFailure();
         }
 
         if (mLibraryLoaded && !mFetchingVariations) {
+            if (FeatureUtilities.isNetworkServiceWarmUpEnabled()) {
+                ChildProcessLauncherHelper.warmUp(ContextUtils.getApplicationContext(), false);
+            }
             if (mAllocateChildConnection) {
-                ChildProcessLauncherHelper.warmUp(ContextUtils.getApplicationContext());
+                ChildProcessLauncherHelper.warmUp(ContextUtils.getApplicationContext(), true);
             }
             onSuccess();
         }
     }
 
     @VisibleForTesting
-    protected Executor getFetchSeedExecutor() {
-        return AsyncTask.THREAD_POOL_EXECUTOR;
-    }
-
-    @VisibleForTesting
     protected Executor getTaskPerThreadExecutor() {
         return runnable -> new Thread(runnable).start();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java
index 2e218e6..de16597d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java
@@ -5,10 +5,13 @@
 package org.chromium.chrome.browser.photo_picker;
 
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.res.AssetFileDescriptor;
 import android.graphics.Bitmap;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
@@ -23,9 +26,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.util.ConversionUtils;
 
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
@@ -38,6 +39,9 @@
     // A tag for logging error messages.
     private static final String TAG = "ImageDecoderHost";
 
+    // A content resolver for providing file descriptors for the images.
+    private ContentResolver mContentResolver;
+
     // The number of successful decodes, per batch.
     private int mSuccessfulDecodes;
 
@@ -95,8 +99,8 @@
      * Class for keeping track of the data involved with each request.
      */
     private static class DecoderServiceParams {
-        // The path to the file containing the bitmap to decode.
-        public String mFilePath;
+        // The URI for the file containing the bitmap to decode.
+        public Uri mUri;
 
         // The requested size (width and height) of the bitmap, once decoded.
         public int mSize;
@@ -107,8 +111,8 @@
         // The timestamp for when the request was sent for decoding.
         long mTimestamp;
 
-        public DecoderServiceParams(String filePath, int size, ImageDecodedCallback callback) {
-            mFilePath = filePath;
+        public DecoderServiceParams(Uri uri, int size, ImageDecodedCallback callback) {
+            mUri = uri;
             mSize = size;
             mCallback = callback;
         }
@@ -138,6 +142,7 @@
             mCallbacks.add(sReadyCallbackForTesting);
         }
         mContext = context;
+        mContentResolver = mContext.getContentResolver();
     }
 
     /**
@@ -164,13 +169,13 @@
     /**
      * Accepts a request to decode a single image. Queues up the request and reports back
      * asynchronously on |callback|.
-     * @param filePath The path to the file to decode.
+     * @param uri The URI of the file to decode.
      * @param size The requested size (width and height) of the resulting bitmap.
      * @param callback The callback to use to communicate the decoding results.
      */
-    public void decodeImage(String filePath, int size, ImageDecodedCallback callback) {
-        DecoderServiceParams params = new DecoderServiceParams(filePath, size, callback);
-        mRequests.put(filePath, params);
+    public void decodeImage(Uri uri, int size, ImageDecodedCallback callback) {
+        DecoderServiceParams params = new DecoderServiceParams(uri, size, callback);
+        mRequests.put(uri.getPath(), params);
         if (mRequests.size() == 1) dispatchNextDecodeImageRequest();
     }
 
@@ -181,7 +186,7 @@
         if (mRequests.entrySet().iterator().hasNext()) {
             DecoderServiceParams params = mRequests.entrySet().iterator().next().getValue();
             params.mTimestamp = SystemClock.elapsedRealtime();
-            dispatchDecodeImageRequest(params.mFilePath, params.mSize);
+            dispatchDecodeImageRequest(params.mUri, params.mSize);
         } else {
             int totalRequests = mSuccessfulDecodes + mFailedDecodesRuntime + mFailedDecodesMemory;
             if (totalRequests > 0) {
@@ -259,13 +264,11 @@
 
     /**
      * Communicates with the server to decode a single bitmap.
-     * @param filePath The path to the image on disk.
+     * @param uri The URI of the image on disk.
      * @param size The requested width and height of the resulting bitmap.
      */
-    private void dispatchDecodeImageRequest(String filePath, int size) {
+    private void dispatchDecodeImageRequest(Uri uri, int size) {
         // Obtain a file descriptor to send over to the sandboxed process.
-        File file = new File(filePath);
-        FileInputStream inputFile = null;
         ParcelFileDescriptor pfd = null;
         Bundle bundle = new Bundle();
 
@@ -273,38 +276,36 @@
         // contents, so we need to obtain a file descriptor to pass over.
         StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
         try {
+            AssetFileDescriptor afd = null;
             try {
-                inputFile = new FileInputStream(file);
-                FileDescriptor fd = inputFile.getFD();
-                pfd = ParcelFileDescriptor.dup(fd);
-                bundle.putParcelable(DecoderService.KEY_FILE_DESCRIPTOR, pfd);
-            } catch (IOException e) {
+                afd = mContentResolver.openAssetFileDescriptor(uri, "r");
+            } catch (FileNotFoundException e) {
                 Log.e(TAG, "Unable to obtain FileDescriptor: " + e);
-                closeRequest(filePath, null, -1);
+                closeRequest(uri.getPath(), null, -1);
+                return;
+            }
+            pfd = afd.getParcelFileDescriptor();
+            if (pfd == null) {
+                closeRequest(uri.getPath(), null, -1);
+                return;
             }
         } finally {
-            try {
-                if (inputFile != null) inputFile.close();
-            } catch (IOException e) {
-                Log.e(TAG, "Unable to close inputFile: " + e);
-            }
             StrictMode.setThreadPolicy(oldPolicy);
         }
 
-        if (pfd == null) return;
-
         // Prepare and send the data over.
-        bundle.putString(DecoderService.KEY_FILE_PATH, filePath);
+        bundle.putString(DecoderService.KEY_FILE_PATH, uri.getPath());
+        bundle.putParcelable(DecoderService.KEY_FILE_DESCRIPTOR, pfd);
         bundle.putInt(DecoderService.KEY_SIZE, size);
         try {
             mIRemoteService.decodeImage(bundle, this);
             pfd.close();
         } catch (RemoteException e) {
             Log.e(TAG, "Communications failed (Remote): " + e);
-            closeRequest(filePath, null, -1);
+            closeRequest(uri.getPath(), null, -1);
         } catch (IOException e) {
             Log.e(TAG, "Communications failed (IO): " + e);
-            closeRequest(filePath, null, -1);
+            closeRequest(uri.getPath(), null, -1);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/FileEnumWorkerTask.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/FileEnumWorkerTask.java
index 5d91aec..789bbdd38 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/FileEnumWorkerTask.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/FileEnumWorkerTask.java
@@ -5,7 +5,11 @@
 package org.chromium.chrome.browser.photo_picker;
 
 import android.Manifest;
+import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
 import android.os.Environment;
 import android.provider.MediaStore;
 
@@ -16,7 +20,6 @@
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 /**
@@ -42,6 +45,9 @@
     // The filter to apply to the list.
     private MimeTypeFilter mFilter;
 
+    // The ContentResolver to use to retrieve image metadata from disk.
+    private ContentResolver mContentResolver;
+
     // The camera directory undir DCIM.
     private static final String SAMPLE_DCIM_SOURCE_SUB_DIRECTORY = "Camera";
 
@@ -50,12 +56,14 @@
      * @param windowAndroid The window wrapper associated with the current activity.
      * @param callback The callback to use to communicate back the results.
      * @param filter The file filter to apply to the list.
+     * @param contentResolver The ContentResolver to use to retrieve image metadata from disk.
      */
-    public FileEnumWorkerTask(
-            WindowAndroid windowAndroid, FilesEnumeratedCallback callback, MimeTypeFilter filter) {
+    public FileEnumWorkerTask(WindowAndroid windowAndroid, FilesEnumeratedCallback callback,
+            MimeTypeFilter filter, ContentResolver contentResolver) {
         mWindowAndroid = windowAndroid;
         mCallback = callback;
         mFilter = filter;
+        mContentResolver = contentResolver;
     }
 
     /**
@@ -67,30 +75,6 @@
     }
 
     /**
-     * Recursively enumerate files in a directory (and subdirectories) and add them to a list.
-     * @param directory The parent directory to recursively traverse.
-     * @param pickerBitmaps The list to add the results to.
-     * @return True if traversing can continue, false if traversing was aborted and should stop.
-     */
-    private boolean traverseDir(File directory, List<PickerBitmap> pickerBitmaps) {
-        File[] files = directory.listFiles(mFilter);
-        if (files == null) return true;
-
-        for (File file : files) {
-            if (isCancelled()) return false;
-
-            if (file.isDirectory()) {
-                if (!traverseDir(file, pickerBitmaps)) return false;
-            } else {
-                pickerBitmaps.add(new PickerBitmap(
-                        file.getPath(), file.lastModified(), PickerBitmap.TileTypes.PICTURE));
-            }
-        }
-
-        return true;
-    }
-
-    /**
      * Enumerates (in the background) the image files on disk. Called on a non-UI thread
      * @param params Ignored, do not use.
      * @return A sorted list of images (by last-modified first).
@@ -103,27 +87,48 @@
 
         List<PickerBitmap> pickerBitmaps = new ArrayList<>();
 
-        // TODO(finnur): Figure out which directories to scan and stop hard coding "Camera" above.
-        File[] sourceDirs = new File[] {
-                getCameraDirectory(),
-                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
-                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
-        };
+        final String[] selectColumns = {MediaStore.Images.Media._ID,
+                MediaStore.Images.Media.DATE_TAKEN, MediaStore.Images.Media.DATA};
 
-        for (File directory : sourceDirs) {
-            if (!traverseDir(directory, pickerBitmaps)) return null;
+        final String whereClause = "(" + MediaStore.Images.Media.DATA + " LIKE ? OR "
+                + MediaStore.Images.Media.DATA + " LIKE ? OR " + MediaStore.Images.Media.DATA
+                + " LIKE ?) AND " + MediaStore.Images.Media.DATA + " NOT LIKE ?";
+
+        final String[] whereArgs = {// Include:
+                getCameraDirectory().toString() + "%",
+                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "%",
+                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+                        + "%",
+                // Exclude low-quality sources, such as the screenshots directory:
+                // TODO(finnur): As of the Q SDK this can be converted to DIRECTORY_SCREENSHOTS.
+                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+                        + "/Screenshots/"
+                        + "%"};
+
+        final String orderBy = MediaStore.Images.Media.DATE_TAKEN + " DESC";
+
+        Cursor imageCursor = mContentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+                selectColumns, whereClause, whereArgs, orderBy);
+
+        while (imageCursor.moveToNext()) {
+            int dataIndex = imageCursor.getColumnIndex(MediaStore.Images.Media.DATA);
+            int dateTakenIndex = imageCursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN);
+            int idIndex = imageCursor.getColumnIndex(MediaStore.Images.ImageColumns._ID);
+            Uri uri = ContentUris.withAppendedId(
+                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageCursor.getInt(idIndex));
+            long dateTaken = imageCursor.getLong(dateTakenIndex);
+            pickerBitmaps.add(new PickerBitmap(uri, dateTaken, PickerBitmap.TileTypes.PICTURE));
         }
+        imageCursor.close();
 
-        Collections.sort(pickerBitmaps);
-
-        pickerBitmaps.add(0, new PickerBitmap("", 0, PickerBitmap.TileTypes.GALLERY));
+        pickerBitmaps.add(0, new PickerBitmap(null, 0, PickerBitmap.TileTypes.GALLERY));
         boolean hasCameraAppAvailable =
                 mWindowAndroid.canResolveActivity(new Intent(MediaStore.ACTION_IMAGE_CAPTURE));
         boolean hasOrCanRequestCameraPermission =
                 mWindowAndroid.hasPermission(Manifest.permission.CAMERA)
                 || mWindowAndroid.canRequestPermission(Manifest.permission.CAMERA);
         if (hasCameraAppAvailable && hasOrCanRequestCameraPermission) {
-            pickerBitmaps.add(0, new PickerBitmap("", 0, PickerBitmap.TileTypes.CAMERA));
+            pickerBitmaps.add(0, new PickerBitmap(null, 0, PickerBitmap.TileTypes.CAMERA));
         }
 
         return pickerBitmaps;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java
index ae7a10ae..ac466f21 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java
@@ -6,6 +6,7 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.net.Uri;
 import android.support.v7.app.AlertDialog;
 
 import org.chromium.base.ActivityState;
@@ -55,7 +56,7 @@
 
         // PhotoPickerListener:
         @Override
-        public void onPhotoPickerUserAction(@PhotoPickerAction int action, String[] photos) {
+        public void onPhotoPickerUserAction(@PhotoPickerAction int action, Uri[] photos) {
             mExternalIntentSelected = false;
             if (action == PhotoPickerAction.LAUNCH_GALLERY
                     || action == PhotoPickerAction.LAUNCH_CAMERA) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmap.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmap.java
index ffc24c8..27f46a29 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmap.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmap.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.photo_picker;
 
+import android.net.Uri;
 import android.support.annotation.IntDef;
 
 import org.chromium.base.ApiCompatibilityUtils;
@@ -26,8 +27,8 @@
         int GALLERY = 2;
     }
 
-    // The file path to the bitmap to show.
-    private String mFilePath;
+    // The URI of the bitmap to show.
+    private Uri mUri;
 
     // When the bitmap was last modified on disk.
     private long mLastModified;
@@ -38,22 +39,22 @@
 
     /**
      * The PickerBitmap constructor.
-     * @param filePath The file path to the bitmap to show.
+     * @param uri The URI for the bitmap to show.
      * @param lastModified When the bitmap was last modified on disk.
      * @param type The type of tile involved.
      */
-    public PickerBitmap(String filePath, long lastModified, @TileTypes int type) {
-        mFilePath = filePath;
+    public PickerBitmap(Uri uri, long lastModified, @TileTypes int type) {
+        mUri = uri;
         mLastModified = lastModified;
         mType = type;
     }
 
     /**
-     * Accessor for the filepath.
-     * @return The file path for this PickerBitmap object.
+     * Accessor for the URI.
+     * @return The URI for this PickerBitmap object.
      */
-    public String getFilePath() {
-        return mFilePath;
+    public Uri getUri() {
+        return mUri;
     }
 
     /**
@@ -61,9 +62,10 @@
      * @return The filename (without the extension and path).
      */
     public String getFilenameWithoutExtension() {
-        int index = mFilePath.lastIndexOf("/");
-        if (index == -1) return mFilePath;
-        return mFilePath.substring(index + 1, mFilePath.length());
+        String filePath = mUri.getPath();
+        int index = filePath.lastIndexOf("/");
+        if (index == -1) return filePath;
+        return filePath.substring(index + 1, filePath.length());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java
index 3ae3759..ecdae499 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java
@@ -59,7 +59,7 @@
                     .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
         }
 
-        if (!TextUtils.equals(mBitmapDetails.getFilePath(), filePath)) {
+        if (!TextUtils.equals(mBitmapDetails.getUri().getPath(), filePath)) {
             return;
         }
 
@@ -87,7 +87,7 @@
             return PickerAdapter.DecodeActions.NO_ACTION;
         }
 
-        String filePath = mBitmapDetails.getFilePath();
+        String filePath = mBitmapDetails.getUri().getPath();
         Bitmap original = mCategoryView.getHighResBitmaps().get(filePath);
         if (original != null) {
             mItemView.initialize(mBitmapDetails, original, false);
@@ -109,14 +109,17 @@
             mItemView.initialize(mBitmapDetails, null, true);
         }
 
-        mCategoryView.getDecoderServiceHost().decodeImage(filePath, size, this);
+        mCategoryView.getDecoderServiceHost().decodeImage(mBitmapDetails.getUri(), size, this);
         return PickerAdapter.DecodeActions.DECODE;
     }
 
     /**
-     * Returns the file path of the current request.
+     * Returns the file path of the current request, or null if no request is in progress for this
+     * holder.
      */
     public String getFilePath() {
-        return mBitmapDetails == null ? null : mBitmapDetails.getFilePath();
+        if (mBitmapDetails == null || mBitmapDetails.type() != PickerBitmap.TileTypes.PICTURE)
+            return null;
+        return mBitmapDetails.getUri().getPath();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
index 58716e6..d18e6cb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
@@ -9,6 +9,7 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
+import android.net.Uri;
 import android.os.SystemClock;
 import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.RecyclerView;
@@ -366,8 +367,8 @@
         }
 
         mEnumStartTime = SystemClock.elapsedRealtime();
-        mWorkerTask = new FileEnumWorkerTask(
-                mActivity.getWindowAndroid(), this, new MimeTypeFilter(mMimeTypes, true));
+        mWorkerTask = new FileEnumWorkerTask(mActivity.getWindowAndroid(), this,
+                new MimeTypeFilter(mMimeTypes, true), mActivity.getContentResolver());
         mWorkerTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
@@ -377,10 +378,10 @@
     private void notifyPhotosSelected() {
         List<PickerBitmap> selectedFiles = mSelectionDelegate.getSelectedItemsAsList();
         Collections.sort(selectedFiles);
-        String[] photos = new String[selectedFiles.size()];
+        Uri[] photos = new Uri[selectedFiles.size()];
         int i = 0;
         for (PickerBitmap bitmap : selectedFiles) {
-            photos[i++] = bitmap.getFilePath();
+            photos[i++] = bitmap.getUri();
         }
 
         executeAction(
@@ -431,7 +432,7 @@
      * @param umaId The UMA value to record with the action.
      */
     private void executeAction(
-            @PhotoPickerListener.PhotoPickerAction int action, String[] photos, int umaId) {
+            @PhotoPickerListener.PhotoPickerAction int action, Uri[] photos, int umaId) {
         mListener.onPhotoPickerUserAction(action, photos);
         mDialog.dismiss();
         UiUtils.onPhotoPickerDismissed();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
index 26f6cf6..de9042a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
@@ -321,6 +321,13 @@
      */
     public static final String PRIORITIZE_BOOTSTRAP_TASKS_KEY = "prioritize_bootstrap_tasks";
 
+    /**
+     * Whether warming up network service is enabled.
+     * Default value is false.
+     */
+    public static final String NETWORK_SERVICE_WARM_UP_ENABLED_KEY =
+            "network_service_warm_up_enabled";
+
     private static class LazyHolder {
         static final ChromePreferenceManager INSTANCE = new ChromePreferenceManager();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelFilter.java
index d29be3b..ec46f7c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelFilter.java
@@ -69,14 +69,19 @@
 
     /**
      * Any of the concrete class can override and define a relationship that links a {@link Tab} to
-     * a list of related {@link Tab}s. By default, this returns an empty unmodifiable list. Note
-     * that the meaning of related can vary depending on the filter being applied.
+     * a list of related {@link Tab}s. By default, this returns an unmodifiable list that only
+     * contains the {@link Tab} with the given id. Note that the meaning of related can vary
+     * depending on the filter being applied.
      * @param tabId Id of the {@link Tab} try to relate with.
      * @return An unmodifiable list of {@link Tab} that relate with the given tab id.
      */
     @NonNull
     public List<Tab> getRelatedTabList(int tabId) {
-        return sEmptyRelatedTabList;
+        Tab tab = TabModelUtils.getTabById(getTabModel(), tabId);
+        if (tab == null) return sEmptyRelatedTabList;
+        List<Tab> relatedTab = new ArrayList<>();
+        relatedTab.add(tab);
+        return Collections.unmodifiableList(relatedTab);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java
index 1c79da4c..651641a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java
@@ -240,8 +240,8 @@
 
     @Override
     public int indexOf(Tab tab) {
-        if (tab == null) return TabList.INVALID_TAB_INDEX;
-        return mGroupIdToGroupIndexMap.get(tab.getId());
+        if (tab == null || tab.isIncognito() != isIncognito()) return TabList.INVALID_TAB_INDEX;
+        return mGroupIdToGroupIndexMap.get(tab.getRootId());
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
index a4349c0..47245a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -85,6 +85,7 @@
     private static Boolean sIsTabGroupsAndroidEnabled;
     private static Boolean sFeedEnabled;
     private static Boolean sServiceManagerForBackgroundPrefetch;
+    private static Boolean sIsNetworkServiceWarmUpEnabled;
 
     private static Boolean sDownloadAutoResumptionEnabledInNative;
 
@@ -206,6 +207,7 @@
         cachePrioritizeBootstrapTasks();
         cacheFeedEnabled();
         cacheServiceManagerForBackgroundPrefetch();
+        cacheNetworkServiceWarmUpEnabled();
 
         if (isDeviceEligibleForTabGroups()) cacheTabGroupsAndroidEnabled();
 
@@ -657,6 +659,29 @@
         return sShouldPrioritizeBootstrapTasks;
     }
 
+    /**
+     * Cache whether warming up network service process is enabled, so that the value
+     * can be made available immediately on next start up.
+     */
+    private static void cacheNetworkServiceWarmUpEnabled() {
+        ChromePreferenceManager.getInstance().writeBoolean(
+                ChromePreferenceManager.NETWORK_SERVICE_WARM_UP_ENABLED_KEY,
+                nativeIsNetworkServiceWarmUpEnabled());
+    }
+
+    /**
+     * @return whether warming up network service is enabled.
+     */
+    public static boolean isNetworkServiceWarmUpEnabled() {
+        if (sIsNetworkServiceWarmUpEnabled == null) {
+            ChromePreferenceManager prefManager = ChromePreferenceManager.getInstance();
+            sIsNetworkServiceWarmUpEnabled = prefManager.readBoolean(
+                    ChromePreferenceManager.NETWORK_SERVICE_WARM_UP_ENABLED_KEY, false);
+        }
+        return sIsNetworkServiceWarmUpEnabled;
+    }
+
     private static native void nativeSetCustomTabVisible(boolean visible);
     private static native void nativeSetIsInMultiWindowMode(boolean isInMultiWindowMode);
+    private static native boolean nativeIsNetworkServiceWarmUpEnabled();
 }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 5169993..35ec5f3 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -135,6 +135,8 @@
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java",
+  "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingState.java",
+  "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingStateCache.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryInfoView.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java",
@@ -1842,11 +1844,17 @@
     "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessNewTabPageProperties.java",
     "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessNewTabPageTopLayout.java",
     "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabObserver.java",
+    "touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java",
+    "touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java",
+    "touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHProperties.java",
+    "touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHView.java",
+    "touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHViewBinder.java",
     "touchless/java/src/org/chromium/chrome/browser/touchless/ui/progressbar/ProgressBarCoordinator.java",
     "touchless/java/src/org/chromium/chrome/browser/touchless/ui/progressbar/ProgressBarMediator.java",
     "touchless/java/src/org/chromium/chrome/browser/touchless/ui/progressbar/ProgressBarProperties.java",
     "touchless/java/src/org/chromium/chrome/browser/touchless/ui/progressbar/ProgressBarView.java",
     "touchless/java/src/org/chromium/chrome/browser/touchless/ui/progressbar/ProgressBarViewBinder.java",
+    "touchless/java/src/org/chromium/chrome/browser/touchless/ui/tooltip/TooltipView.java",
   ]
 } else {
   chrome_java_sources += [ "touchless/fallback/java/src/org/chromium/chrome/browser/touchless/TouchlessDelegate.java" ]
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java
index a6bdb22e..342d30b2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -70,7 +70,8 @@
     private final ChromeTabbedActivityTestRule mActivityTestRule;
     private final AtomicReference<WebContents> mWebContentsRef = new AtomicReference<>();
     private TestInputMethodManagerWrapper mInputMethodManagerWrapper;
-    private Provider<AccessorySheetData> mSheetSuggestionsProvider = new PropertyProvider<>();
+    private PropertyProvider<AccessorySheetData> mSheetSuggestionsProvider =
+            new PropertyProvider<>();
 
     private EmbeddedTestServer mEmbeddedTestServer;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryIntegrationTest.java
index 90ccbc0a..12bb2fc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryIntegrationTest.java
@@ -63,11 +63,13 @@
     public void testPasswordSheetIsAvailable() throws InterruptedException {
         mHelper.loadTestPage(false);
 
-        Assert.assertNotNull("Password Sheet should be bound to accessory sheet.",
-                mActivityTestRule.getActivity()
-                        .getManualFillingController()
-                        .getMediatorForTesting()
-                        .getPasswordAccessorySheet());
+        CriteriaHelper.pollUiThread(()
+                                            -> mActivityTestRule.getActivity()
+                                                       .getManualFillingController()
+                                                       .getMediatorForTesting()
+                                                       .getOrCreatePasswordSheet()
+                        != null,
+                "Password Sheet should be bound to accessory sheet.");
     }
 
     @Test
@@ -76,11 +78,13 @@
     public void testPasswordSheetIsAvailableInExperimentalUi() throws InterruptedException {
         mHelper.loadTestPage(false);
 
-        Assert.assertNotNull("Password Sheet should be bound to accessory sheet.",
-                mActivityTestRule.getActivity()
-                        .getManualFillingController()
-                        .getMediatorForTesting()
-                        .getPasswordAccessorySheet());
+        CriteriaHelper.pollUiThread(()
+                                            -> mActivityTestRule.getActivity()
+                                                       .getManualFillingController()
+                                                       .getMediatorForTesting()
+                                                       .getOrCreatePasswordSheet()
+                        != null,
+                "Password Sheet should be bound to accessory sheet.");
     }
 
     @Test
@@ -95,7 +99,7 @@
                 mActivityTestRule.getActivity()
                         .getManualFillingController()
                         .getMediatorForTesting()
-                        .getPasswordAccessorySheet());
+                        .getOrCreatePasswordSheet());
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java
index 76f8377..b41fc710 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
 
+import android.net.Uri;
 import android.os.Build;
 import android.support.test.filters.LargeTest;
 import android.support.v7.widget.RecyclerView;
@@ -68,7 +69,7 @@
 
     // The final set of photos picked by the dialog. Can be an empty array, if
     // nothing was selected.
-    private String[] mLastSelectedPhotos;
+    private Uri[] mLastSelectedPhotos;
 
     // The list of currently selected photos (built piecemeal).
     private List<PickerBitmap> mCurrentPhotoSelection;
@@ -86,12 +87,12 @@
     public void setUp() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
         mTestFiles = new ArrayList<>();
-        mTestFiles.add(new PickerBitmap("a", 5L, PickerBitmap.TileTypes.PICTURE));
-        mTestFiles.add(new PickerBitmap("b", 4L, PickerBitmap.TileTypes.PICTURE));
-        mTestFiles.add(new PickerBitmap("c", 3L, PickerBitmap.TileTypes.PICTURE));
-        mTestFiles.add(new PickerBitmap("d", 2L, PickerBitmap.TileTypes.PICTURE));
-        mTestFiles.add(new PickerBitmap("e", 1L, PickerBitmap.TileTypes.PICTURE));
-        mTestFiles.add(new PickerBitmap("f", 0L, PickerBitmap.TileTypes.PICTURE));
+        mTestFiles.add(new PickerBitmap(Uri.parse("a"), 5L, PickerBitmap.TileTypes.PICTURE));
+        mTestFiles.add(new PickerBitmap(Uri.parse("b"), 4L, PickerBitmap.TileTypes.PICTURE));
+        mTestFiles.add(new PickerBitmap(Uri.parse("c"), 3L, PickerBitmap.TileTypes.PICTURE));
+        mTestFiles.add(new PickerBitmap(Uri.parse("d"), 2L, PickerBitmap.TileTypes.PICTURE));
+        mTestFiles.add(new PickerBitmap(Uri.parse("e"), 1L, PickerBitmap.TileTypes.PICTURE));
+        mTestFiles.add(new PickerBitmap(Uri.parse("f"), 0L, PickerBitmap.TileTypes.PICTURE));
         PickerCategoryView.setTestFiles(mTestFiles);
 
         DecoderServiceHost.setReadyCallback(this);
@@ -100,7 +101,7 @@
     // PhotoPickerDialog.PhotoPickerListener:
 
     @Override
-    public void onPhotoPickerUserAction(@PhotoPickerAction int action, String[] photos) {
+    public void onPhotoPickerUserAction(@PhotoPickerAction int action, Uri[] photos) {
         mLastActionRecorded = action;
         mLastSelectedPhotos = photos != null ? photos.clone() : null;
         if (mLastSelectedPhotos != null) Arrays.sort(mLastSelectedPhotos);
@@ -233,7 +234,7 @@
 
         Assert.assertEquals(1, mLastSelectedPhotos.length);
         Assert.assertEquals(PhotoPickerAction.PHOTOS_SELECTED, mLastActionRecorded);
-        Assert.assertEquals(mTestFiles.get(1).getFilePath(), mLastSelectedPhotos[0]);
+        Assert.assertEquals(mTestFiles.get(1).getUri().getPath(), mLastSelectedPhotos[0]);
 
         dismissDialog();
     }
@@ -255,9 +256,9 @@
 
         Assert.assertEquals(3, mLastSelectedPhotos.length);
         Assert.assertEquals(PhotoPickerAction.PHOTOS_SELECTED, mLastActionRecorded);
-        Assert.assertEquals(mTestFiles.get(0).getFilePath(), mLastSelectedPhotos[0]);
-        Assert.assertEquals(mTestFiles.get(2).getFilePath(), mLastSelectedPhotos[1]);
-        Assert.assertEquals(mTestFiles.get(4).getFilePath(), mLastSelectedPhotos[2]);
+        Assert.assertEquals(mTestFiles.get(0).getUri().getPath(), mLastSelectedPhotos[0]);
+        Assert.assertEquals(mTestFiles.get(2).getUri().getPath(), mLastSelectedPhotos[1]);
+        Assert.assertEquals(mTestFiles.get(4).getUri().getPath(), mLastSelectedPhotos[2]);
 
         dismissDialog();
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
index 429a9b4..b93f512 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
@@ -43,6 +43,7 @@
 import org.chromium.base.UserDataHost;
 import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeKeyboardVisibilityDelegate;
@@ -67,7 +68,6 @@
 import org.chromium.ui.test.util.modelutil.FakeViewProvider;
 
 import java.lang.ref.WeakReference;
-import java.util.Map;
 
 /**
  * Controller tests for the root controller for interactions with the manual filling UI.
@@ -75,15 +75,15 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
 @EnableFeatures({ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY,
-        ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY})
-@DisableFeatures(ChromeFeatureList.EXPERIMENTAL_UI)
+        ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY,
+        ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID})
+@DisableFeatures({ChromeFeatureList.EXPERIMENTAL_UI})
 public class ManualFillingControllerTest {
     @Mock
     private ChromeWindow mMockWindow;
     @Mock
     private ChromeActivity mMockActivity;
-    @Mock
-    private WebContents mMockWebContents;
+    private WebContents mLastMockWebContents;
     @Mock
     private ViewGroup mMockContentView;
     @Mock
@@ -120,10 +120,13 @@
         when(mMockWindow.getKeyboardDelegate()).thenReturn(mMockKeyboard);
         when(mMockWindow.getActivity()).thenReturn(new WeakReference<>(mMockActivity));
         when(mMockActivity.getTabModelSelector()).thenReturn(mMockTabModelSelector);
+        when(mMockActivity.getActivityTabProvider()).thenReturn(mock(ActivityTabProvider.class));
         ChromeFullscreenManager fullscreenManager = new ChromeFullscreenManager(mMockActivity, 0);
         when(mMockActivity.getFullscreenManager()).thenReturn(fullscreenManager);
         when(mMockActivity.getResources()).thenReturn(mMockResources);
         when(mMockActivity.findViewById(android.R.id.content)).thenReturn(mMockContentView);
+        mLastMockWebContents = mock(WebContents.class);
+        when(mMockActivity.getCurrentWebContents()).then(i -> mLastMockWebContents);
         when(mMockResources.getDimensionPixelSize(anyInt())).thenReturn(48);
         PasswordAccessorySheetCoordinator.IconProvider.getInstance().setIconForTesting(mMockIcon);
         when(mMockKeyboardAccessoryView.getTabLayout()).thenReturn(mMockTabSwitcherView);
@@ -136,7 +139,6 @@
     public void testCreatesValidSubComponents() {
         assertThat(mController, is(notNullValue()));
         assertThat(mController.getMediatorForTesting(), is(notNullValue()));
-        assertThat(mController.getMediatorForTesting().getModelForTesting(), is(notNullValue()));
         assertThat(mController.getKeyboardAccessory(), is(notNullValue()));
         assertThat(mController.getMediatorForTesting().getAccessorySheet(), is(notNullValue()));
     }
@@ -158,8 +160,7 @@
         assertThat(getTabLayout().getModelForTesting().get(TABS).size(), is(0));
         assertThat(accessorySheetModel.get(AccessorySheetProperties.TABS).size(), is(0));
 
-        mController.getMediatorForTesting().addTab(
-                new KeyboardAccessoryData.Tab("Passwords", null, null, 0, 0, null));
+        mController.getMediatorForTesting().getOrCreatePasswordSheet();
 
         verify(mMockTabListObserver)
                 .onItemRangeInserted(getTabLayout().getModelForTesting().get(TABS), 0, 1);
@@ -172,27 +173,25 @@
     @Test
     public void testAddingBrowserTabsCreatesValidAccessoryState() {
         ManualFillingMediator mediator = mController.getMediatorForTesting();
-        Map<Tab, ManualFillingMediator.AccessoryState> model = mediator.getModelForTesting();
-        assertThat(model.size(), is(0));
-
+        ManualFillingStateCache cache = mediator.getStateCacheForTesting();
         // Emulate adding a browser tab. Expect the model to have another entry.
         Tab firstTab = addTab(mediator, 1111, null);
-        assertThat(model.size(), is(1));
-        assertThat(model.get(firstTab), notNullValue());
+        ManualFillingState firstState = cache.getStateFor(firstTab);
+        assertThat(firstState, notNullValue());
 
         // Emulate adding a second browser tab. Expect the model to have another entry.
         Tab secondTab = addTab(mediator, 2222, firstTab);
-        assertThat(model.size(), is(2));
-        assertThat(model.get(secondTab), notNullValue());
+        ManualFillingState secondState = cache.getStateFor(secondTab);
+        assertThat(secondState, notNullValue());
 
-        assertThat(model.get(firstTab), not(equalTo(model.get(secondTab))));
+        assertThat(firstState, not(equalTo(secondState)));
     }
 
     @Test
     public void testPasswordItemsPersistAfterSwitchingBrowserTabs() {
         ManualFillingMediator mediator = mController.getMediatorForTesting();
-        Provider<AccessorySheetData> firstTabProvider = new PropertyProvider<>();
-        Provider<AccessorySheetData> secondTabProvider = new PropertyProvider<>();
+        PropertyProvider<AccessorySheetData> firstTabProvider = new PropertyProvider<>();
+        PropertyProvider<AccessorySheetData> secondTabProvider = new PropertyProvider<>();
 
         // Simulate opening a new tab which automatically triggers the registration:
         Tab firstTab = addTab(mediator, 1111, null);
@@ -292,6 +291,7 @@
     @Test
     public void testPasswordTabRestoredWhenClosingTabIsUndone() {
         ManualFillingMediator mediator = mController.getMediatorForTesting();
+        ManualFillingStateCache cache = mediator.getStateCacheForTesting();
         assertThat(getTabLayout().getModelForTesting().get(TABS).size(), is(0));
 
         // Create a new tab with a passwords tab:
@@ -299,11 +299,11 @@
         mController.registerPasswordProvider(new PropertyProvider<>());
         assertThat(getTabLayout().getModelForTesting().get(TABS).size(), is(1));
 
-        // Simulate closing the tab:
+        // Simulate closing the tab (uncommitted):
         mediator.getTabModelObserverForTesting().willCloseTab(tab, false);
         mediator.getTabObserverForTesting().onHidden(tab, TabHidingType.CHANGED_TABS);
-        // Temporary removes the tab, but keeps it in memory so it can be brought back on undo:
-        assertThat(getTabLayout().getModelForTesting().get(TABS).size(), is(0));
+        cache.getStateFor(mLastMockWebContents).getWebContentsObserverForTesting().wasHidden();
+        mLastMockWebContents = null;
 
         // Simulate undo closing the tab and selecting it:
         mediator.getTabModelObserverForTesting().tabClosureUndone(tab);
@@ -313,24 +313,10 @@
 
         // Simulate closing the tab and committing to it (i.e. wait out undo message):
         closeTab(mediator, tab, null);
-        mediator.getTabModelObserverForTesting().tabClosureCommitted(tab);
         assertThat(getTabLayout().getModelForTesting().get(TABS).size(), is(0));
     }
 
     @Test
-    public void testRecoversFromInvalidState() {
-        ManualFillingMediator mediator = mController.getMediatorForTesting();
-
-        // Open a tab but pretend that the states became inconsistent.
-        Tab tab = addTab(mediator, 1111, null);
-        mediator.getModelForTesting().get(tab).mPasswordAccessorySheet =
-                new PasswordAccessorySheetCoordinator(mMockActivity, null);
-
-        // Create a new tab with a passwords tab:
-        addTab(mediator, 1111, tab);
-    }
-
-    @Test
     public void testTreatNeverProvidedActionsAsEmptyActionList() {
         ManualFillingMediator mediator = mController.getMediatorForTesting();
         PropertyModel keyboardAccessoryModel =
@@ -369,7 +355,7 @@
         assertThat(keyboardAccessoryModel.get(KeyboardAccessoryProperties.BAR_ITEMS).size(), is(1));
 
         // Create and switch to a new tab:
-        Tab secondTab = addTab(mediator, 1111, tab);
+        Tab secondTab = addTab(mediator, 2222, tab);
         PropertyProvider<Action[]> provider = new PropertyProvider<>(GENERATE_PASSWORD_AUTOMATIC);
         mController.registerActionProvider(provider);
 
@@ -393,6 +379,7 @@
     @Test
     public void testDestroyingTabCleansModelForThisTab() {
         ManualFillingMediator mediator = mController.getMediatorForTesting();
+        ManualFillingStateCache cache = mediator.getStateCacheForTesting();
         PropertyModel keyboardAccessoryModel =
                 mediator.getKeyboardAccessory().getMediatorForTesting().getModelForTesting();
         PropertyModel accessorySheetModel = mController.getMediatorForTesting()
@@ -400,10 +387,10 @@
                                                     .getMediatorForTesting()
                                                     .getModelForTesting();
 
-        Provider<AccessorySheetData> firstTabProvider = new PropertyProvider<>();
+        PropertyProvider<AccessorySheetData> firstTabProvider = new PropertyProvider<>();
         PropertyProvider<Action[]> firstActionProvider =
                 new PropertyProvider<>(GENERATE_PASSWORD_AUTOMATIC);
-        Provider<AccessorySheetData> secondTabProvider = new PropertyProvider<>();
+        PropertyProvider<AccessorySheetData> secondTabProvider = new PropertyProvider<>();
         PropertyProvider<Action[]> secondActionProvider =
                 new PropertyProvider<>(GENERATE_PASSWORD_AUTOMATIC);
 
@@ -439,15 +426,16 @@
         assertThat(getFirstKeyboardActionTitle(), is("2BKept"));
 
         // The other tabs data should be gone.
-        ManualFillingMediator.AccessoryState oldState = mediator.getModelForTesting().get(firstTab);
+        ManualFillingState oldState = cache.getStateFor(firstTab);
         if (oldState == null)
             return; // Having no state is fine - it would be completely destroyed then.
 
-        assertThat(oldState.mActionsProvider, nullValue());
+        assertThat(oldState.getActionsProvider(), nullValue());
 
-        if (oldState.mPasswordAccessorySheet == null)
+        if (oldState.getPasswordAccessorySheet() == null)
             return; // Having no password sheet is fine - it would be completely destroyed then.
-        assertThat(oldState.mPasswordAccessorySheet.getSheetDataPiecesForTesting().size(), is(0));
+        assertThat(
+                oldState.getPasswordAccessorySheet().getSheetDataPiecesForTesting().size(), is(0));
     }
 
     @Test
@@ -464,8 +452,9 @@
         mController.onResume();
 
         assertThat(mediator.getKeyboardAccessory().isShown(), is(false));
-        // |getPasswordAccessorySheet| creates a sheet if the state allows it. Here, it shouldn't.
-        assertThat(mediator.getPasswordAccessorySheet(), is(nullValue()));
+        // |getOrCreatePasswordSheet| creates a sheet if the state allows it. Here, it shouldn't.
+        assertThat(
+                mediator.getOrCreatePasswordSheet().getSheetDataPiecesForTesting().size(), is(0));
     }
 
     @Test
@@ -475,8 +464,7 @@
         when(mMockKeyboard.isSoftKeyboardShowing(eq(mMockActivity), any())).thenReturn(true);
 
         // Show the accessory bar for the default dimensions (300x80@2.f).
-        getTabLayout().getTabSwitchingDelegate().addTab(
-                new KeyboardAccessoryData.Tab("Passwords", null, null, 0, 0, null));
+        mediator.getOrCreatePasswordSheet();
         mediator.showWhenKeyboardIsVisible();
         assertThat(mediator.getKeyboardAccessory().isShown(), is(true));
 
@@ -506,7 +494,7 @@
         closeTab(mediator, mock(Tab.class), null);
 
         // Without any tab, there should be no state that would allow creating a sheet.
-        assertThat(mediator.getPasswordAccessorySheet(), is(nullValue()));
+        assertThat(mediator.getOrCreatePasswordSheet(), is(nullValue()));
     }
 
     @Test
@@ -515,8 +503,7 @@
         KeyboardAccessoryCoordinator accessory =
                 mController.getMediatorForTesting().getKeyboardAccessory();
         addTab(mController.getMediatorForTesting(), 1234, null);
-        mController.getMediatorForTesting().addTab(
-                new KeyboardAccessoryData.Tab("Passwords", null, null, 0, 0, null));
+        mController.getMediatorForTesting().getOrCreatePasswordSheet();
         accessory.requestShowing();
         assertThat(mController.isFillingViewShown(null), is(false));
 
@@ -539,17 +526,21 @@
      * @return Returns a mock of the newly added {@link Tab}. Provides |getId()|.
      */
     private Tab addTab(ManualFillingMediator mediator, int id, @Nullable Tab lastTab) {
+        ManualFillingStateCache cache = mediator.getStateCacheForTesting();
         int lastId = INVALID_TAB_ID;
         if (lastTab != null) {
             lastId = lastTab.getId();
             mediator.getTabObserverForTesting().onHidden(lastTab, TabHidingType.CHANGED_TABS);
+            cache.getStateFor(mLastMockWebContents).getWebContentsObserverForTesting().wasHidden();
         }
         Tab tab = mock(Tab.class);
         when(tab.getId()).thenReturn(id);
         when(tab.getUserDataHost()).thenReturn(mUserDataHost);
-        when(tab.getWebContents()).thenReturn(mMockWebContents);
+        mLastMockWebContents = mock(WebContents.class);
+        when(tab.getWebContents()).thenReturn(mLastMockWebContents);
+        cache.getStateFor(tab).getWebContentsObserverForTesting().wasShown();
         when(tab.getContentView()).thenReturn(mMockContentView);
-        when(mMockActivity.getActivityTab()).thenReturn(tab);
+        when(mMockActivity.getActivityTabProvider().getActivityTab()).thenReturn(tab);
         when(mMockTabModelSelector.getCurrentTab()).thenReturn(tab);
         mediator.getTabModelObserverForTesting().didAddTab(tab, FROM_BROWSER_ACTIONS);
         mediator.getTabObserverForTesting().onShown(tab, FROM_NEW);
@@ -565,11 +556,15 @@
      * @param to The mocked {@link Tab} to be switched to. Needs |getId()|.
      */
     private void switchTab(ManualFillingMediator mediator, @Nullable Tab from, Tab to) {
+        ManualFillingStateCache cache = mediator.getStateCacheForTesting();
         int lastId = INVALID_TAB_ID;
         if (from != null) {
             lastId = from.getId();
             mediator.getTabObserverForTesting().onHidden(from, TabHidingType.CHANGED_TABS);
+            cache.getStateFor(mLastMockWebContents).getWebContentsObserverForTesting().wasHidden();
         }
+        mLastMockWebContents = to.getWebContents();
+        cache.getStateFor(to).getWebContentsObserverForTesting().wasShown();
         when(mMockTabModelSelector.getCurrentTab()).thenReturn(to);
         mediator.getTabModelObserverForTesting().didSelectTab(to, FROM_USER, lastId);
         mediator.getTabObserverForTesting().onShown(to, FROM_USER);
@@ -582,13 +577,19 @@
      * @param next A mocked {@link Tab} to be switched to. Needs |getId()|. May be null.
      */
     private void closeTab(ManualFillingMediator mediator, Tab tabToBeClosed, @Nullable Tab next) {
+        ManualFillingStateCache cache = mediator.getStateCacheForTesting();
         mediator.getTabModelObserverForTesting().willCloseTab(tabToBeClosed, false);
         mediator.getTabObserverForTesting().onHidden(tabToBeClosed, TabHidingType.CHANGED_TABS);
+        cache.getStateFor(mLastMockWebContents).getWebContentsObserverForTesting().wasHidden();
+        mLastMockWebContents = null;
         if (next != null) {
             when(mMockTabModelSelector.getCurrentTab()).thenReturn(next);
+            cache.getStateFor(next).getWebContentsObserverForTesting().wasShown();
+            mLastMockWebContents = next.getWebContents();
             mediator.getTabModelObserverForTesting().didSelectTab(
                     next, FROM_CLOSE, tabToBeClosed.getId());
         }
+        mediator.getTabModelObserverForTesting().tabClosureCommitted(tabToBeClosed);
         mediator.getTabObserverForTesting().onDestroyed(tabToBeClosed);
     }
 
@@ -596,8 +597,8 @@
         DisplayAndroid mockDisplay = mock(DisplayAndroid.class);
         when(mockDisplay.getDipScale()).thenReturn(density);
         when(mMockWindow.getDisplay()).thenReturn(mockDisplay);
-        when(mMockWebContents.getHeight()).thenReturn(heightDp);
-        when(mMockWebContents.getWidth()).thenReturn(widthDp);
+        when(mLastMockWebContents.getHeight()).thenReturn(heightDp);
+        when(mLastMockWebContents.getWidth()).thenReturn(widthDp);
     }
 
     /**
@@ -611,8 +612,8 @@
      */
     private void simulateOrientationChange(
             ManualFillingMediator mediator, float density, int width, int height) {
-        int oldHeight = mMockWebContents.getHeight();
-        int oldWidth = mMockWebContents.getWidth();
+        int oldHeight = mLastMockWebContents.getHeight();
+        int oldWidth = mLastMockWebContents.getWidth();
         int newHeight = (int) (density * height);
         int newWidth = (int) (density * width);
         setContentAreaDimensions(density, width, height);
@@ -636,7 +637,7 @@
     }
 
     private String getFirstPassword(ManualFillingMediator mediator) {
-        PasswordAccessorySheetCoordinator passwordSheet = mediator.getPasswordAccessorySheet();
+        PasswordAccessorySheetCoordinator passwordSheet = mediator.getOrCreatePasswordSheet();
         assert passwordSheet != null;
         assert passwordSheet.getSheetDataPiecesForTesting() != null;
         assert passwordSheet.getSheetDataPiecesForTesting().size() > 0;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/init/AsyncInitTaskRunnerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/init/AsyncInitTaskRunnerTest.java
index 1d460d5e..6a1fdad 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/init/AsyncInitTaskRunnerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/init/AsyncInitTaskRunnerTest.java
@@ -15,6 +15,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
@@ -25,6 +26,7 @@
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.library_loader.LoaderErrors;
 import org.chromium.base.library_loader.ProcessInitException;
+import org.chromium.base.task.PostTask;
 import org.chromium.base.task.test.ShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.components.variations.firstrun.VariationsSeedFetcher;
@@ -55,6 +57,7 @@
         LibraryLoader.setLibraryLoaderForTesting(mLoader);
         mVariationsSeedFetcher = mock(VariationsSeedFetcher.class);
         VariationsSeedFetcher.setVariationsSeedFetcherForTesting(mVariationsSeedFetcher);
+        PostTask.setPrenativeThreadPoolExecutorForTesting(new RoboExecutorService());
 
         mLatch = new CountDownLatch(1);
         mRunner = spy(new AsyncInitTaskRunner() {
@@ -67,10 +70,6 @@
                 mLatch.countDown();
             }
             @Override
-            protected Executor getFetchSeedExecutor() {
-                return new RoboExecutorService();
-            }
-            @Override
             protected Executor getTaskPerThreadExecutor() {
                 return new RoboExecutorService();
             }
@@ -79,6 +78,11 @@
         when(mRunner.shouldFetchVariationsSeedDuringFirstRun()).thenReturn(true);
     }
 
+    @After
+    public void tearDown() throws InterruptedException {
+        PostTask.resetPrenativeThreadPoolExecutorForTesting();
+    }
+
     @Test
     public void libraryLoaderOnlyTest()
             throws InterruptedException, ProcessInitException, ExecutionException {
diff --git a/chrome/android/touchless/java/res/drawable/ic_cursor_navigation.xml b/chrome/android/touchless/java/res/drawable/ic_cursor_navigation.xml
new file mode 100644
index 0000000..99867785
--- /dev/null
+++ b/chrome/android/touchless/java/res/drawable/ic_cursor_navigation.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="14.0"
+    android:viewportWidth="14.0"
+    tools:ignore="NewApi">
+    <path
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"
+        android:pathData="M13.75,5.898L0.25,0.25L5.905,13.75H6.64L8.62,8.62L13.75,6.633V5.898ZM6.227,10.645L3.047,3.047L10.645,6.22L8.073,7.218L7.457,7.457L7.218,8.08L6.227,10.645Z" />
+</vector>
diff --git a/chrome/android/touchless/java/res/drawable/ic_spatial_navigation.xml b/chrome/android/touchless/java/res/drawable/ic_spatial_navigation.xml
new file mode 100644
index 0000000..152908ee
--- /dev/null
+++ b/chrome/android/touchless/java/res/drawable/ic_spatial_navigation.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="14.0"
+    android:viewportWidth="18.0"
+    tools:ignore="NewApi">
+    <path
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"
+        android:pathData="M2.25,0.25H15.75C16.575,0.25 17.25,0.925 17.25,1.75V12.25C17.25,13.075 16.575,13.75 15.75,13.75H2.25C1.425,13.75 0.75,13.075 0.75,12.25V1.75C0.75,0.925 1.425,0.25 2.25,0.25ZM2.25,12.25H15.75V1.75H2.25V12.25ZM9.007,2.125L7.5,4H10.5L9.007,2.125ZM13.5,8.5V5.5L15.375,7.008L13.5,8.5ZM4.5,5.5L2.625,7.008L4.5,8.5V5.5ZM7.5,10H10.5L9.007,11.875L7.5,10Z" />
+</vector>
diff --git a/chrome/android/touchless/java/res/drawable/ic_zoom_in.xml b/chrome/android/touchless/java/res/drawable/ic_zoom_in.xml
new file mode 100644
index 0000000..2d373f7e
--- /dev/null
+++ b/chrome/android/touchless/java/res/drawable/ic_zoom_in.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="14.0"
+    android:viewportWidth="14.0"
+    tools:ignore="NewApi">
+    <path
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"
+        android:pathData="M13.368,12.25L9.07,7.952C9.648,7.15 10,6.182 10,5.125C10,2.432 7.818,0.25 5.125,0.25C2.432,0.25 0.25,2.432 0.25,5.125C0.25,7.818 2.432,10 5.125,10C6.182,10 7.15,9.648 7.952,9.07L12.25,13.368L13.368,12.25ZM5.125,8.5C3.257,8.5 1.75,6.992 1.75,5.125C1.75,3.257 3.257,1.75 5.125,1.75C6.992,1.75 8.5,3.257 8.5,5.125C8.5,6.992 6.992,8.5 5.125,8.5ZM7.375,4.375H5.875V2.875H4.375V4.375H2.875V5.875H4.375V7.375H5.875V5.875H7.375V4.375Z" />
+</vector>
diff --git a/chrome/android/touchless/java/res/drawable/ic_zoom_out.xml b/chrome/android/touchless/java/res/drawable/ic_zoom_out.xml
new file mode 100644
index 0000000..32c8b8c
--- /dev/null
+++ b/chrome/android/touchless/java/res/drawable/ic_zoom_out.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="14.0"
+    android:viewportWidth="14.0"
+    tools:ignore="NewApi">
+    <path
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"
+        android:pathData="M9.086,7.962C9.003,8.078 8.915,8.19 8.823,8.297L8.297,8.823C8.19,8.915 8.078,9.003 7.962,9.086L7.563,8.688L8.688,7.563L9.086,7.962ZM9.086,7.962C9.662,7.161 10,6.18 10,5.125C10,2.432 7.818,0.25 5.125,0.25C2.432,0.25 0.25,2.432 0.25,5.125C0.25,7.818 2.432,10 5.125,10C6.18,10 7.161,9.662 7.962,9.086L12.25,13.368L13.368,12.25L9.086,7.962ZM5.125,8.5C3.257,8.5 1.75,6.992 1.75,5.125C1.75,3.257 3.257,1.75 5.125,1.75C6.992,1.75 8.5,3.257 8.5,5.125C8.5,6.992 6.992,8.5 5.125,8.5ZM7,4.375H3.25V5.875H7V4.375Z" />
+</vector>
diff --git a/chrome/android/touchless/java/res/drawable/notouch_key_functions_iph_item_bg.xml b/chrome/android/touchless/java/res/drawable/notouch_key_functions_iph_item_bg.xml
new file mode 100644
index 0000000..7bf83f6
--- /dev/null
+++ b/chrome/android/touchless/java/res/drawable/notouch_key_functions_iph_item_bg.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <stroke
+        android:width="1dp"
+        android:color="@color/notouch_key_functions_iph_item_border" />
+
+    <corners
+        android:bottomLeftRadius="4dp"
+        android:bottomRightRadius="4dp"
+        android:topLeftRadius="4dp"
+        android:topRightRadius="4dp" />
+</shape>
diff --git a/chrome/android/touchless/java/res/drawable/notouch_tooltip_bg.xml b/chrome/android/touchless/java/res/drawable/notouch_tooltip_bg.xml
new file mode 100644
index 0000000..2a06a08b
--- /dev/null
+++ b/chrome/android/touchless/java/res/drawable/notouch_tooltip_bg.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <solid
+        android:color="@color/notouch_tooltip_background" />
+
+    <corners
+        android:bottomLeftRadius="8dp"
+        android:bottomRightRadius="8dp"
+        android:topLeftRadius="8dp"
+        android:topRightRadius="8dp" />
+</shape>
diff --git a/chrome/android/touchless/java/res/layout/notouch_key_functions_view.xml b/chrome/android/touchless/java/res/layout/notouch_key_functions_view.xml
new file mode 100644
index 0000000..2f92be3
--- /dev/null
+++ b/chrome/android/touchless/java/res/layout/notouch_key_functions_view.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<org.chromium.chrome.browser.touchless.ui.iph.KeyFunctionsIPHView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    tools:ignore="ContentDescription,HardcodedText">
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layoutDirection="ltr"
+        android:orientation="horizontal">
+
+        <LinearLayout
+            android:layout_width="@dimen/notouch_key_functions_tooltip_item_width"
+            android:layout_height="@dimen/notouch_key_functions_tooltip_item_height"
+            android:background="@drawable/notouch_key_functions_iph_item_bg"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:paddingStart="@dimen/notouch_key_functions_tooltip_item_padding"
+                android:text="1"
+                android:textAppearance="@style/TextAppearance.NoTouchKeyFunctionsIPH"/>
+
+            <ImageView
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:paddingBottom="@dimen/notouch_key_functions_tooltip_item_image_vertical_padding"
+                android:paddingEnd="@dimen/notouch_key_functions_tooltip_item_padding"
+                android:paddingTop="@dimen/notouch_key_functions_tooltip_item_image_vertical_padding"
+                android:scaleType="centerInside"
+                app:srcCompat="@drawable/ic_zoom_out"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="@dimen/notouch_key_functions_tooltip_item_width"
+            android:layout_height="@dimen/notouch_key_functions_tooltip_item_height"
+            android:layout_marginEnd="@dimen/notouch_key_functions_tooltip_item_horizontal_margin"
+            android:layout_marginStart="@dimen/notouch_key_functions_tooltip_item_horizontal_margin"
+            android:background="@drawable/notouch_key_functions_iph_item_bg"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:paddingStart="4dp"
+                android:text="2"
+                android:textAppearance="@style/TextAppearance.NoTouchKeyFunctionsIPH"/>
+
+            <ImageView
+                android:id="@+id/navigation_mode_image"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:paddingBottom="@dimen/notouch_key_functions_tooltip_item_image_vertical_padding"
+                android:paddingEnd="4dp"
+                android:paddingTop="@dimen/notouch_key_functions_tooltip_item_image_vertical_padding"
+                android:scaleType="centerInside"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="@dimen/notouch_key_functions_tooltip_item_width"
+            android:layout_height="@dimen/notouch_key_functions_tooltip_item_height"
+            android:background="@drawable/notouch_key_functions_iph_item_bg"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:paddingStart="@dimen/notouch_key_functions_tooltip_item_padding"
+                android:text="3"
+                android:textAppearance="@style/TextAppearance.NoTouchKeyFunctionsIPH"/>
+
+            <ImageView
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:paddingBottom="@dimen/notouch_key_functions_tooltip_item_image_vertical_padding"
+                android:paddingEnd="@dimen/notouch_key_functions_tooltip_item_padding"
+                android:paddingTop="@dimen/notouch_key_functions_tooltip_item_image_vertical_padding"
+                android:scaleType="centerInside"
+                app:srcCompat="@drawable/ic_zoom_in"/>
+
+        </LinearLayout>
+    </LinearLayout>
+</org.chromium.chrome.browser.touchless.ui.iph.KeyFunctionsIPHView>
\ No newline at end of file
diff --git a/chrome/android/touchless/java/res/layout/notouch_tooltip_view.xml b/chrome/android/touchless/java/res/layout/notouch_tooltip_view.xml
new file mode 100644
index 0000000..3089b05
--- /dev/null
+++ b/chrome/android/touchless/java/res/layout/notouch_tooltip_view.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="wrap_content"
+             android:layout_height="wrap_content"
+             android:alpha=".9"
+             android:background="@drawable/notouch_tooltip_bg"
+             android:focusable="false"
+             android:focusableInTouchMode="false"
+             android:padding="@dimen/notouch_tooltip_padding"/>
\ No newline at end of file
diff --git a/chrome/android/touchless/java/res/values-v17/colors.xml b/chrome/android/touchless/java/res/values-v17/colors.xml
index 15effdb..c76b22e 100644
--- a/chrome/android/touchless/java/res/values-v17/colors.xml
+++ b/chrome/android/touchless/java/res/values-v17/colors.xml
@@ -6,4 +6,6 @@
 <resources>
     <color name="notouch_progress_bar_foreground">@color/google_blue_50</color>
     <color name="notouch_progress_bar_text">@color/modern_grey_800</color>
+    <color name="notouch_tooltip_background">@color/modern_grey_800</color>
+    <color name="notouch_key_functions_iph_item_border">@android:color/white</color>
 </resources>
diff --git a/chrome/android/touchless/java/res/values-v17/dimens.xml b/chrome/android/touchless/java/res/values-v17/dimens.xml
new file mode 100644
index 0000000..90bd804
--- /dev/null
+++ b/chrome/android/touchless/java/res/values-v17/dimens.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <dimen name="touchless_new_tab_view_padding_horizontal">12dp</dimen>
+    <dimen name="notouch_tooltip_padding">8dp</dimen>
+    <dimen name="notouch_tooltip_margin_bottom">16dp</dimen>
+    <dimen name="notouch_key_functions_tooltip_item_width">40dp</dimen>
+    <dimen name="notouch_key_functions_tooltip_item_height">24dp</dimen>
+    <dimen name="notouch_key_functions_tooltip_item_padding">4dp</dimen>
+    <dimen name="notouch_key_functions_tooltip_item_image_vertical_padding">5dp</dimen>
+    <dimen name="notouch_key_functions_tooltip_item_horizontal_margin">4dp</dimen>
+</resources>
diff --git a/chrome/android/touchless/java/res/values-v17/styles.xml b/chrome/android/touchless/java/res/values-v17/styles.xml
index 61f80f2..838951a 100644
--- a/chrome/android/touchless/java/res/values-v17/styles.xml
+++ b/chrome/android/touchless/java/res/values-v17/styles.xml
@@ -8,4 +8,9 @@
         <item name="android:textSize">14sp</item>
         <item name="android:textColor">@color/notouch_progress_bar_text</item>
     </style>
-</resources>
+
+    <style name="TextAppearance.NoTouchKeyFunctionsIPH">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">@android:color/white</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/chrome/android/touchless/java/res/values/dimens.xml b/chrome/android/touchless/java/res/values/dimens.xml
deleted file mode 100644
index d5bc56e..0000000
--- a/chrome/android/touchless/java/res/values/dimens.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-<resources>
-    <dimen name="touchless_new_tab_view_padding_horizontal">12dp</dimen>
-</resources>
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java
index 78123322..e059c1e2 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java
@@ -22,8 +22,10 @@
 import org.chromium.chrome.browser.tab.TabBuilder;
 import org.chromium.chrome.browser.tab.TabRedirectHandler;
 import org.chromium.chrome.browser.tab.TabState;
+import org.chromium.chrome.browser.touchless.ui.iph.KeyFunctionsIPHCoordinator;
 import org.chromium.chrome.browser.touchless.ui.progressbar.ProgressBarCoordinator;
 import org.chromium.chrome.browser.touchless.ui.progressbar.ProgressBarView;
+import org.chromium.chrome.browser.touchless.ui.tooltip.TooltipView;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.common.Referrer;
 import org.chromium.ui.base.PageTransition;
@@ -37,8 +39,10 @@
     // Time at which an intent was received and handled.
     private long mIntentHandlingTimeMs;
 
-    private ProgressBarView mProgressBarView;
+    private KeyFunctionsIPHCoordinator mKeyFunctionsIPHCoordinator;
     private ProgressBarCoordinator mProgressBarCoordinator;
+    private TooltipView mTooltipView;
+    private ProgressBarView mProgressBarView;
 
     /**
      * Internal class which performs the intent handling operations delegated by IntentHandler.
@@ -113,8 +117,10 @@
     @Override
     public void initializeState() {
         super.initializeState();
+        mKeyFunctionsIPHCoordinator =
+                new KeyFunctionsIPHCoordinator(mTooltipView, getActivityTabProvider());
         mProgressBarCoordinator =
-                new ProgressBarCoordinator(getActivityTabProvider(), mProgressBarView);
+                new ProgressBarCoordinator(mProgressBarView, getActivityTabProvider());
 
         // By this point if we were going to restore a URL from savedInstanceState we would already
         // have done so.
@@ -193,15 +199,15 @@
     protected void doLayoutInflation() {
         super.doLayoutInflation();
         ViewGroup coordinatorLayout = (ViewGroup) findViewById(R.id.coordinator);
+        mTooltipView = new TooltipView(this);
         mProgressBarView = new ProgressBarView(this);
+        coordinatorLayout.addView(mTooltipView);
         coordinatorLayout.addView(mProgressBarView);
     }
 
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
-        if (mProgressBarCoordinator != null) {
-            mProgressBarCoordinator.onKeyEvent();
-        }
+        if (mProgressBarCoordinator != null) mProgressBarCoordinator.onKeyEvent();
         return super.dispatchKeyEvent(event);
     }
 
@@ -214,8 +220,7 @@
     @Override
     protected void onDestroyInternal() {
         super.onDestroyInternal();
-        if (mProgressBarCoordinator != null) {
-            mProgressBarCoordinator.destroy();
-        }
+        if (mKeyFunctionsIPHCoordinator != null) mKeyFunctionsIPHCoordinator.destroy();
+        if (mProgressBarCoordinator != null) mProgressBarCoordinator.destroy();
     }
 }
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java
new file mode 100644
index 0000000..06a1f093
--- /dev/null
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java
@@ -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.
+
+package org.chromium.chrome.browser.touchless.ui.iph;
+
+import android.view.LayoutInflater;
+
+import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.chrome.browser.touchless.ui.tooltip.TooltipView;
+import org.chromium.chrome.touchless.R;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+
+/**
+ * A helper class that controls the UI for key functions In-Product Help in NoTouch mode.
+ */
+public class KeyFunctionsIPHCoordinator {
+    private KeyFunctionsIPHMediator mMediator;
+
+    public KeyFunctionsIPHCoordinator(
+            TooltipView tooltipView, ActivityTabProvider activityTabProvider) {
+        PropertyModel model = new PropertyModel.Builder(KeyFunctionsIPHProperties.ALL_KEYS)
+                                      .with(KeyFunctionsIPHProperties.TOOLTIP_VIEW, tooltipView)
+                                      .build();
+        KeyFunctionsIPHView view =
+                (KeyFunctionsIPHView) LayoutInflater.from(tooltipView.getContext())
+                        .inflate(R.layout.notouch_key_functions_view, null);
+        PropertyModelChangeProcessor.create(model, view, KeyFunctionsIPHViewBinder::bind);
+        mMediator = new KeyFunctionsIPHMediator(model, activityTabProvider);
+    }
+
+    public void destroy() {
+        mMediator.destroy();
+    }
+}
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java
new file mode 100644
index 0000000..f2b5d826
--- /dev/null
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java
@@ -0,0 +1,83 @@
+// 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.touchless.ui.iph;
+
+import org.chromium.base.task.PostTask;
+import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
+import org.chromium.ui.base.CursorVisibilityObserver;
+import org.chromium.ui.base.TouchlessEventHandler;
+import org.chromium.ui.modelutil.PropertyModel;
+
+import java.util.concurrent.FutureTask;
+
+/**
+ * Controls the UI model for key functions IPH.
+ */
+public class KeyFunctionsIPHMediator implements CursorVisibilityObserver {
+    private final PropertyModel mModel;
+    private final KeyFunctionsIPHTabObserver mKeyFunctionsIPHTabObserver;
+    private FutureTask mHideTask;
+    private boolean mHasShownOnSpatNav;
+    private boolean mHasShownOnFallback;
+
+    private static final long DISPLAY_DURATION_MS = 1500;
+
+    KeyFunctionsIPHMediator(PropertyModel model, ActivityTabProvider activityTabProvider) {
+        mModel = model;
+        mKeyFunctionsIPHTabObserver = new KeyFunctionsIPHTabObserver(activityTabProvider);
+        TouchlessEventHandler.addCursorVisibilityObserver(this);
+    }
+
+    @Override
+    public void onCursorVisibilityChanged(boolean isCursorVisible) {
+        show(isCursorVisible);
+    }
+
+    private void show(boolean isCursorVisible) {
+        // TODO(crbug.com/942665): Populate this.
+        boolean pageOptimizedForMobile = true;
+        if ((!isCursorVisible && mHasShownOnSpatNav && pageOptimizedForMobile)
+                || (isCursorVisible && mHasShownOnFallback)) {
+            return;
+        }
+
+        if (isCursorVisible) {
+            mHasShownOnFallback = true;
+        } else {
+            mHasShownOnSpatNav = true;
+        }
+        if (mHideTask != null) mHideTask.cancel(false);
+        mHideTask = new FutureTask<Void>(() -> {
+            mModel.set(KeyFunctionsIPHProperties.IS_VISIBLE, false);
+            return null;
+        });
+        PostTask.postDelayedTask(UiThreadTaskTraits.DEFAULT, mHideTask, DISPLAY_DURATION_MS);
+        mModel.set(KeyFunctionsIPHProperties.IS_CURSOR_VISIBLE, isCursorVisible);
+        mModel.set(KeyFunctionsIPHProperties.IS_VISIBLE, true);
+    }
+
+    void destroy() {
+        TouchlessEventHandler.removeCursorVisibilityObserver(this);
+        mKeyFunctionsIPHTabObserver.destroy();
+        if (mHideTask != null) mHideTask.cancel(false);
+    }
+
+    private class KeyFunctionsIPHTabObserver extends ActivityTabProvider.ActivityTabTabObserver {
+        KeyFunctionsIPHTabObserver(ActivityTabProvider tabProvider) {
+            super(tabProvider);
+        }
+
+        @Override
+        public void onPageLoadFinished(Tab tab, String url) {
+            if (tab.isNativePage()) return;
+
+            if (tab.isShowingErrorPage()) return;
+
+            show(false);
+        }
+    }
+}
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHProperties.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHProperties.java
new file mode 100644
index 0000000..18c6b95
--- /dev/null
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHProperties.java
@@ -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.
+
+package org.chromium.chrome.browser.touchless.ui.iph;
+
+import org.chromium.chrome.browser.touchless.ui.tooltip.TooltipView;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * This class holds property keys used in key functions IPH {@link PropertyModel}.
+ */
+public class KeyFunctionsIPHProperties {
+    public static final PropertyModel.WritableBooleanPropertyKey IS_CURSOR_VISIBLE =
+            new PropertyModel.WritableBooleanPropertyKey();
+
+    public static final PropertyModel.WritableBooleanPropertyKey IS_VISIBLE =
+            new PropertyModel.WritableBooleanPropertyKey();
+
+    public static final PropertyModel.WritableObjectPropertyKey<TooltipView> TOOLTIP_VIEW =
+            new PropertyModel.WritableObjectPropertyKey<>();
+
+    public static final PropertyKey[] ALL_KEYS = {IS_CURSOR_VISIBLE, IS_VISIBLE, TOOLTIP_VIEW};
+}
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHView.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHView.java
new file mode 100644
index 0000000..5f435e4
--- /dev/null
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHView.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.touchless.ui.iph;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import org.chromium.chrome.browser.touchless.ui.tooltip.TooltipView;
+import org.chromium.chrome.touchless.R;
+
+/**
+ * View responsible for displaying key functions IPH.
+ */
+public class KeyFunctionsIPHView extends FrameLayout {
+    private TooltipView mTooltipView;
+    private ImageView mNavigationModeImageView;
+
+    public KeyFunctionsIPHView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public KeyFunctionsIPHView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mNavigationModeImageView = findViewById(R.id.navigation_mode_image);
+    }
+
+    void setTooltipView(TooltipView hostView) {
+        mTooltipView = hostView;
+    }
+
+    void show(boolean isCursorVisible) {
+        if (isCursorVisible) {
+            mNavigationModeImageView.setImageResource(R.drawable.ic_spatial_navigation);
+        } else {
+            mNavigationModeImageView.setImageResource(R.drawable.ic_cursor_navigation);
+        }
+        mTooltipView.show(this);
+    }
+
+    void hide() {
+        mTooltipView.hide();
+    }
+}
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHViewBinder.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHViewBinder.java
new file mode 100644
index 0000000..35a2d57
--- /dev/null
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHViewBinder.java
@@ -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.
+
+package org.chromium.chrome.browser.touchless.ui.iph;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * This class binds the key functions IPH UI model and view.
+ */
+public class KeyFunctionsIPHViewBinder {
+    public static void bind(
+            PropertyModel model, KeyFunctionsIPHView keyFunctionsIPHView, PropertyKey propertyKey) {
+        if (KeyFunctionsIPHProperties.TOOLTIP_VIEW == propertyKey) {
+            keyFunctionsIPHView.setTooltipView(model.get(KeyFunctionsIPHProperties.TOOLTIP_VIEW));
+        } else if (KeyFunctionsIPHProperties.IS_VISIBLE == propertyKey) {
+            if (model.get(KeyFunctionsIPHProperties.IS_VISIBLE))
+                keyFunctionsIPHView.show(model.get(KeyFunctionsIPHProperties.IS_CURSOR_VISIBLE));
+            else
+                keyFunctionsIPHView.hide();
+        }
+    }
+}
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/progressbar/ProgressBarCoordinator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/progressbar/ProgressBarCoordinator.java
index 6030a80..7150b47 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/progressbar/ProgressBarCoordinator.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/progressbar/ProgressBarCoordinator.java
@@ -22,7 +22,7 @@
      * eTLD+1.
      */
     public ProgressBarCoordinator(
-            ActivityTabProvider activityTabProvider, ProgressBarView progressBarView) {
+            ProgressBarView progressBarView, ActivityTabProvider activityTabProvider) {
         PropertyModel model = new PropertyModel.Builder(ProgressBarProperties.ALL_KEYS).build();
         PropertyModelChangeProcessor.create(model, progressBarView, ProgressBarViewBinder::bind);
         mMediator = new ProgressBarMediator(model, activityTabProvider);
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/tooltip/TooltipView.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/tooltip/TooltipView.java
new file mode 100644
index 0000000..ffe39b0f
--- /dev/null
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/tooltip/TooltipView.java
@@ -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.
+
+package org.chromium.chrome.browser.touchless.ui.tooltip;
+
+import android.content.Context;
+import android.support.design.widget.CoordinatorLayout;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import org.chromium.chrome.touchless.R;
+
+/**
+ * Responsible for displaying tooltips in NoTouch mode.
+ */
+public class TooltipView extends FrameLayout {
+    private ViewGroup mContainerView;
+
+    public TooltipView(Context context) {
+        super(context);
+        init();
+    }
+
+    public TooltipView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public TooltipView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    private void init() {
+        CoordinatorLayout.LayoutParams layoutParams =
+                new CoordinatorLayout.LayoutParams(CoordinatorLayout.LayoutParams.WRAP_CONTENT,
+                        CoordinatorLayout.LayoutParams.WRAP_CONTENT);
+        layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+        layoutParams.bottomMargin =
+                (int) getResources().getDimension(R.dimen.notouch_tooltip_margin_bottom);
+        setLayoutParams(layoutParams);
+        mContainerView = (ViewGroup) inflate(getContext(), R.layout.notouch_tooltip_view, null);
+        mContainerView.setVisibility(GONE);
+        addView(mContainerView);
+    }
+
+    public void show(View view) {
+        mContainerView.removeAllViews();
+        mContainerView.addView(view);
+        mContainerView.setVisibility(VISIBLE);
+    }
+
+    public void hide() {
+        mContainerView.setVisibility(GONE);
+    }
+}
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index ebdf71b9..93fd9be4 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -8834,14 +8834,17 @@
     <if expr="chromeos">
       <!-- CrOSUsb Notification -->
       <message name="IDS_CROSUSB_DEVICE_DETECTED_NOTIFICATION" desc="Content for notification shown to the user when a USB device gets plugged in.">
-        Connect this device to Linux
+        Connect <ph name="USB_DEVICE_NAME">$1<ex>Nexus 5</ex></ph> to Linux
       </message>
       <message name="IDS_CROSUSB_DEVICE_DETECTED_NOTIFICATION_TITLE" desc="Title for notification shown to the user when a USB device gets plugged in.">
-        <ph name="USB_DEVICE_NAME">$1<ex>Nexus 5</ex></ph> detected
+        USB device detected
       </message>
       <message name="IDS_CROSUSB_NOTIFICATION_BUTTON_CONNECT_TO_LINUX" desc="Label for notification button shown to the user when a USB device gets plugged in to allowed Linux to use the device">
         Connect
       </message>
+      <message name="IDS_CROSUSB_NOTIFICATION_BUTTON_VIEW_SETTINGS" desc="Label for notification button shown to the user when a USB device gets plugged in to view settings in the Settings app">
+        View Settings
+      </message>
       <message name="IDS_CROSUSB_UNKNOWN_DEVICE_FROM_MANUFACTURER" desc="String describing an unknown device in notification shown when a USB device gets plugged in.">
         USB device from <ph name="MANUFACTURER_NAME">$1<ex>Google</ex></ph>
       </message>
@@ -9373,9 +9376,32 @@
       <message name="IDS_WEBAUTHN_PIN_ENTRY_ERROR_TOO_SHORT" desc="Error message. Displayed when the user attempts to set a PIN code for their security key (an external physical device for user authentication), and the PIN chosen by them is too short.">
         PIN is too short.
       </message>
+      <message name="IDS_WEBAUTHN_PIN_ENTRY_ERROR_FAILED_RETRIES" desc="Error message. Displayed when the user enters the wrong PIN code for their security key (an external physical device for user authentication)">
+        {NUM_ATTEMPTS, plural,
+          =1 {Incorrect PIN. You have only one attempt remaining.}
+          other {Incorrect PIN. You have # attempts remaining.}}
+      </message>
+      <message name="IDS_WEBAUTHN_PIN_ENTRY_ERROR_FAILED" desc="Error message. Displayed when the user attempts to enter the PIN code for their security key (an external physical device for user authentication), but the entered PIN is incorrect.">
+        Incorrect PIN.
+      </message>
+      <message name="IDS_WEBAUTHN_PIN_SETUP_ERROR_FAILED" desc="Error message. Displayed when the user attempts to set up a PIN code for their security key (an external physical device for user authentication), and but the entered PIN contains invalid characters.">
+        PIN contains invalid characters.
+      </message>
+      <message name="IDS_WEBAUTHN_PIN_TAP_AGAIN_DESCRIPTION" desc="Description of a dialog shown after the user first activated their security key (an external physical device for user authentication) and provided the PIN for it.">
+        Tap your security key again to complete the request.
+      </message>
       <message name="IDS_WEBAUTHN_PIN_ENTRY_ERROR_MISMATCH" desc="Error message. Displayed when the user attempts to set a PIN code for their security key (an external physical device for user authentication), and the confirmation value does not match the first value they entered.">
         Confirmation does not match.
       </message>
+      <message name="IDS_WEBAUTHN_CLIENT_PIN_SOFT_BLOCK_DESCRIPTION" desc="Description in the error dialog shown after their security key (an external physical device for user authentication) was blocked due to the user entering a wrong security key PIN too many times">
+        That security key is temporarily locked due to too many incorrect PIN attempts. Remove it and reinsert to unlock.
+      </message>
+      <message name="IDS_WEBAUTHN_CLIENT_PIN_HARD_BLOCK_DESCRIPTION" desc="Description in the error dialog shown after their security key (an external physical device for user authentication) was blocked due to the user entering a wrong security key PIN too many times">
+        That security key is locked due to too many incorrect PIN attempts. It must be reset in order to perform any PIN-based operations.
+      </message>
+      <message name="IDS_WEBAUTHN_CLIENT_PIN_AUTHENTICATOR_REMOVED_DESCRIPTION" desc="Description in the error dialog shown after their security key (an external physical device for user authentication) was removed while the application was waiting for the security key PIN to be entered.">
+        Your security key has been removed.
+      </message>
    </if>
    <if expr="is_macosx">
      <message name="IDS_WEBAUTHN_TOUCH_ID_TITLE" desc="Title of the dialog shown when the user tries to sign in with Touch ID." meaning="'Touch ID' is the fingerprint recognition feature in macOS. Try to refer Apple support documentation in the target language for the appropriate product name translation.">
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index c71dbbe..65e0aa0 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -555,13 +555,16 @@
       Replace your Linux apps and files with a previous backup
     </message>
     <message name="IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LABEL" desc="Label for managing shared USB devices.">
-      USB Device preferences
+      USB preferences
     </message>
-    <message name="IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LIST_HEADING" desc="Label for list of options to share USB devices.">
-      USB Devices
+    <message name="IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LIST_EMPTY_MESSAGE" desc="Message shown when there are no avaiable USB devices.">
+      Available USB devices will appear here.
     </message>
     <message name="IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_DESCRIPTION" desc="Description for managing shared USB devices.">
-      Share a USB device with Linux by turning the toggle on. Sharing will only last until the device is detached.
+      Give Linux permission to access USB devices. Linux won't remember a usb device after it's removed.
+    </message>
+    <message name="IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_EXTRA_DESCRIPTION" desc="Extra description for managing shared USB devices.">
+       Only some devices are currently supported.
     </message>
 
     <!-- Android Apps Page -->
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 12266b2..655c227 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1537,8 +1537,7 @@
     {"enable-suggestions-with-substring-match",
      flag_descriptions::kSuggestionsWithSubStringMatchName,
      flag_descriptions::kSuggestionsWithSubStringMatchDescription, kOsAll,
-     SINGLE_VALUE_TYPE(
-         autofill::switches::kEnableSuggestionsWithSubstringMatch)},
+     FEATURE_VALUE_TYPE(autofill::features::kAutofillTokenPrefixMatching)},
     {"lcd-text-aa", flag_descriptions::kLcdTextName,
      flag_descriptions::kLcdTextDescription, kOsDesktop,
      ENABLE_DISABLE_VALUE_TYPE(switches::kEnableLCDText,
@@ -2886,6 +2885,13 @@
      flag_descriptions::kOmniboxUIUnboldSuggestionTextDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(omnibox::kUIExperimentUnboldSuggestionText)},
 
+#if defined(OS_ANDROID)
+    {"omnibox-zero-suggestions-on-ntp",
+     flag_descriptions::kOmniboxZeroSuggestionsOnNTPName,
+     flag_descriptions::kOmniboxZeroSuggestionsOnNTPDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(omnibox::kZeroSuggestionsOnNTP)},
+#endif
+
     {"omnibox-material-design-weather-icons",
      flag_descriptions::kOmniboxMaterialDesignWeatherIconsName,
      flag_descriptions::kOmniboxMaterialDesignWeatherIconsDescription,
@@ -3323,6 +3329,10 @@
     {"enable-tab-grid-layout", flag_descriptions::kTabGridLayoutAndroidName,
      flag_descriptions::kTabGridLayoutAndroidDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kTabGridLayoutAndroid)},
+
+    {"enable-tab-groups", flag_descriptions::kTabGroupsAndroidName,
+     flag_descriptions::kTabGroupsAndroidDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kTabGroupsAndroid)},
 #endif  // OS_ANDROID
 
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/android/feature_utilities.cc b/chrome/browser/android/feature_utilities.cc
index 6a3491c..b9abd17c 100644
--- a/chrome/browser/android/feature_utilities.cc
+++ b/chrome/browser/android/feature_utilities.cc
@@ -9,6 +9,8 @@
 #include "chrome/browser/ntp_snippets/content_suggestions_service_factory.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/ntp_snippets/content_suggestions_service.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/network_service_util.h"
 #include "services/metrics/public/cpp/ukm_source.h"
 
 using base::android::JavaParamRef;
@@ -51,3 +53,8 @@
   is_in_multi_window_mode = j_is_in_multi_window_mode;
 }
 
+static jboolean JNI_FeatureUtilities_IsNetworkServiceWarmUpEnabled(
+    JNIEnv* env) {
+  return content::IsOutOfProcessNetworkService() &&
+         base::FeatureList::IsEnabled(features::kWarmUpNetworkProcess);
+}
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index b30f894..726befb2 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -655,6 +655,10 @@
         <include name="IDR_SET_TIME_HTML" file="resources\chromeos\set_time\set_time.html" type="BINDATA" />
         <include name="IDR_SET_TIME_CSS" file="resources\chromeos\set_time\set_time.css" type="BINDATA" />
         <include name="IDR_SET_TIME_JS" file="resources\chromeos\set_time\set_time.js" type="BINDATA" />
+        <include name="IDR_MD_SET_TIME_HTML" file="resources\chromeos\md_set_time\set_time.html" type="BINDATA" compress="gzip" />
+        <include name="IDR_MD_SET_TIME_JS" file="resources\chromeos\md_set_time\set_time.js" type="BINDATA" compress="gzip" />
+        <include name="IDR_MD_SET_TIME_BROWSER_PROXY_HTML" file="resources\chromeos\md_set_time\set_time_browser_proxy.html" type="BINDATA" compress="gzip" />
+        <include name="IDR_MD_SET_TIME_BROWSER_PROXY_JS" file="resources\chromeos\md_set_time\set_time_browser_proxy.js" type="BINDATA" compress="gzip" />
       </if>
       <if expr="chromeos">
         <if expr="_google_chrome">
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index f8a7854b..8847e02 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2141,7 +2141,6 @@
 
     // Please keep this in alphabetical order.
     static const char* const kSwitchNames[] = {
-      autofill::switches::kEnableSuggestionsWithSubstringMatch,
       autofill::switches::kIgnoreAutocompleteOffForAutofill,
       autofill::switches::kShowAutofillSignatures,
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/chrome_navigation_browsertest.cc b/chrome/browser/chrome_navigation_browsertest.cc
index f25ff94..3f81386 100644
--- a/chrome/browser/chrome_navigation_browsertest.cc
+++ b/chrome/browser/chrome_navigation_browsertest.cc
@@ -643,20 +643,20 @@
   EXPECT_TRUE(subframe_host->GetLastCommittedURL().is_empty());
 
   // Now try navigating to a URL that tries to redirect to the error page URL
-  // and make sure the redirect is blocked, resulting in an error page for the
-  // redirect URL and not the error_url destination. Note that DidStopLoading
-  // will still fire after the redirect causes its own error page, so
-  // TestNavigationObserver can be used to wait for it.
+  // and make sure the navigation is ignored. Note that DidStopLoading will
+  // still fire, so TestNavigationObserver can be used to wait for it.
   GURL redirect_to_error_url(
       embedded_test_server()->GetURL("/server-redirect?" + error_url.spec()));
   content::TestNavigationObserver observer(web_contents);
   EXPECT_TRUE(ExecuteScript(
       web_contents, "location.href = '" + redirect_to_error_url.spec() + "';"));
   observer.Wait();
-  EXPECT_EQ(redirect_to_error_url, web_contents->GetLastCommittedURL());
+  EXPECT_EQ(url, web_contents->GetLastCommittedURL());
   EXPECT_EQ(
-      content::PAGE_TYPE_ERROR,
+      content::PAGE_TYPE_NORMAL,
       web_contents->GetController().GetLastCommittedEntry()->GetPageType());
+  // Check the pending URL is not left in the address bar.
+  EXPECT_EQ(url, web_contents->GetVisibleURL());
 }
 
 // This test ensures that navigating to a page that returns an error code and
diff --git a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter.cc b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter.cc
index 9c55f360..0dfdb96 100644
--- a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter.cc
+++ b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter.cc
@@ -64,11 +64,10 @@
 }  // namespace
 
 std::unique_ptr<base::DictionaryValue> ConvertGoldenInputToProcessorInput(
-    const ConsistencyGoldenInput& input) {
-  // Random date representing the last time the policies were updated,
-  // used whenever the last_updated field is not specified in the input proto.
-  base::Time default_last_updated =
-      utils::TimeFromString("1 Jan 2018 10:00 GMT+0300");
+    ConsistencyGoldenInput input) {
+  // An arbitrary date representing the last time the policies were updated,
+  // since the tests won't take this into account for now.
+  base::Time last_updated = utils::TimeFromString("1 Jan 2018 10:00 GMT+0300");
   base::TimeDelta resets_at =
       input.has_usage_limit_resets_at()
           ? utils::CreateTime(input.usage_limit_resets_at().hour(),
@@ -89,9 +88,7 @@
                           window_limit.starts_at().minute()),
         utils::CreateTime(window_limit.ends_at().hour(),
                           window_limit.ends_at().minute()),
-        window_limit.has_last_updated_millis()
-            ? base::Time::FromJavaTime(window_limit.last_updated_millis())
-            : default_last_updated);
+        last_updated);
   }
 
   /* End Window Limits data */
@@ -103,37 +100,16 @@
         policy.get(),
         ConvertGoldenDayToProcessorDay(usage_limit.effective_day()),
         base::TimeDelta::FromMinutes(usage_limit.usage_quota_mins()),
-        usage_limit.has_last_updated_millis()
-            ? base::Time::FromJavaTime(usage_limit.last_updated_millis())
-            : default_last_updated);
+        last_updated);
   }
 
   /* End Usage Limits data */
-  /* Begin Overrides data */
-
-  for (const ConsistencyGoldenOverride& override_entry : input.overrides()) {
-    if (override_entry.action() == UNLOCK_UNTIL_LOCK_DEADLINE) {
-      utils::AddOverrideWithDuration(
-          policy.get(), usage_time_limit::TimeLimitOverride::Action::kUnlock,
-          base::Time::FromJavaTime(override_entry.created_at_millis()),
-          base::TimeDelta::FromMilliseconds(override_entry.duration_millis()));
-    } else {
-      utils::AddOverride(
-          policy.get(),
-          override_entry.action() == LOCK
-              ? usage_time_limit::TimeLimitOverride::Action::kLock
-              : usage_time_limit::TimeLimitOverride::Action::kUnlock,
-          base::Time::FromJavaTime(override_entry.created_at_millis()));
-    }
-  }
-
-  /* End Overrides data */
 
   return policy;
 }
 
 ConsistencyGoldenOutput ConvertProcessorOutputToGoldenOutput(
-    const usage_time_limit::State& state) {
+    usage_time_limit::State state) {
   ConsistencyGoldenOutput golden_output;
 
   golden_output.set_is_locked(state.is_locked);
@@ -142,8 +118,7 @@
   golden_output.set_next_active_policy(
       ConvertProcessorPolicyToGoldenPolicy(state.next_state_active_policy));
 
-  if (state.is_time_usage_limit_enabled &&
-      golden_output.active_policy() != OVERRIDE) {
+  if (state.is_time_usage_limit_enabled) {
     golden_output.set_remaining_quota_millis(
         state.remaining_usage.InMilliseconds());
   }
@@ -156,35 +131,5 @@
   return golden_output;
 }
 
-base::Optional<usage_time_limit::State>
-GenerateUnlockUsageLimitOverrideStateFromInput(
-    const ConsistencyGoldenInput& input) {
-  ConsistencyGoldenOverride* usage_limit_override = nullptr;
-  for (ConsistencyGoldenOverride override_entry : input.overrides()) {
-    if (override_entry.action() == UNLOCK_USAGE_LIMIT &&
-        (!usage_limit_override ||
-         override_entry.created_at_millis() >
-             usage_limit_override->created_at_millis())) {
-      usage_limit_override = &override_entry;
-    }
-  }
-
-  if (!usage_limit_override)
-    return base::nullopt;
-
-  usage_time_limit::State previous_state;
-  previous_state.is_locked = true;
-  previous_state.active_policy = usage_time_limit::ActivePolicies::kUsageLimit;
-  previous_state.is_time_usage_limit_enabled = true;
-  previous_state.remaining_usage = base::TimeDelta::FromMinutes(0);
-
-  // Usage limit started one minute before the override was created.
-  previous_state.time_usage_limit_started =
-      base::Time::FromJavaTime(usage_limit_override->created_at_millis()) -
-      base::TimeDelta::FromMinutes(1);
-
-  return previous_state;
-}
-
 }  // namespace time_limit_consistency
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter.h b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter.h
index 1357471f..5d55e1a 100644
--- a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter.h
+++ b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter.h
@@ -10,7 +10,6 @@
 
 #include <memory>
 
-#include "base/optional.h"
 #include "chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/consistency_golden.pb.h"
 #include "chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h"
 
@@ -24,21 +23,12 @@
 // Converts the input part of a consistency golden case to the structure used by
 // the time limit processor.
 std::unique_ptr<base::DictionaryValue> ConvertGoldenInputToProcessorInput(
-    const ConsistencyGoldenInput& input);
+    ConsistencyGoldenInput input);
 
 // Converts the output struct generated by the time limit processor to the
 // consistency golden output proto.
 ConsistencyGoldenOutput ConvertProcessorOutputToGoldenOutput(
-    const usage_time_limit::State& state);
-
-// Generates a State struct representing the previous state required by the
-// processor if there is a UNLOCK_USAGE_LIMIT override present. The generated
-// state simulates being locked by usage limit since one minute before the
-// override was created.
-// If the override is of another type, base::nullopt will be returned.
-base::Optional<usage_time_limit::State>
-GenerateUnlockUsageLimitOverrideStateFromInput(
-    const ConsistencyGoldenInput& input);
+    usage_time_limit::State state);
 
 }  // namespace time_limit_consistency
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter_unittest.cc b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter_unittest.cc
index 0c035e0..711c3ee 100644
--- a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter_unittest.cc
+++ b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter_unittest.cc
@@ -47,17 +47,15 @@
       utils::CreateTimeLimitPolicy(base::TimeDelta::FromHours(6));
 
   // First window: Wednesday, 22:30 to 8:00
-  consistency_utils::AddWindowLimitEntryToGoldenInput(
-      &input, WEDNESDAY, consistency_utils::TimeOfDay({22, 30}),
-      consistency_utils::TimeOfDay({8, 0}), base::nullopt);
+  consistency_utils::AddWindowLimitEntryToGoldenInput(&input, WEDNESDAY, 22, 30,
+                                                      8, 0);
   utils::AddTimeWindowLimit(expected_output.get(), utils::kWednesday,
                             utils::CreateTime(22, 30), utils::CreateTime(8, 0),
                             kTestLastUpdated);
 
   // Second window: Saturday, 18:45 to 22:30
-  consistency_utils::AddWindowLimitEntryToGoldenInput(
-      &input, SATURDAY, consistency_utils::TimeOfDay({18, 45}),
-      consistency_utils::TimeOfDay({22, 30}), base::nullopt);
+  consistency_utils::AddWindowLimitEntryToGoldenInput(&input, SATURDAY, 18, 45,
+                                                      22, 30);
   utils::AddTimeWindowLimit(expected_output.get(), utils::kSaturday,
                             utils::CreateTime(18, 45),
                             utils::CreateTime(22, 30), kTestLastUpdated);
@@ -68,25 +66,6 @@
   EXPECT_TRUE(*actual_output == *expected_output);
 }
 
-TEST_F(ConsistencyGoldenConverterTest, ConvertInputWithBedtimesLastUpdated) {
-  ConsistencyGoldenInput input;
-  std::unique_ptr<base::DictionaryValue> expected_output =
-      utils::CreateTimeLimitPolicy(base::TimeDelta::FromHours(6));
-
-  // First window: Wednesday, 22:30 to 8:00
-  consistency_utils::AddWindowLimitEntryToGoldenInput(
-      &input, WEDNESDAY, consistency_utils::TimeOfDay({22, 30}),
-      consistency_utils::TimeOfDay({8, 0}), kTestTimestamp);
-  utils::AddTimeWindowLimit(expected_output.get(), utils::kWednesday,
-                            utils::CreateTime(22, 30), utils::CreateTime(8, 0),
-                            base::Time::FromJavaTime(kTestTimestamp));
-
-  std::unique_ptr<base::DictionaryValue> actual_output =
-      ConvertGoldenInputToProcessorInput(input);
-
-  EXPECT_TRUE(*actual_output == *expected_output);
-}
-
 TEST_F(ConsistencyGoldenConverterTest, ConvertInputWithUsageLimit) {
   ConsistencyGoldenInput input;
   std::unique_ptr<base::DictionaryValue> expected_output =
@@ -96,14 +75,12 @@
   input.mutable_usage_limit_resets_at()->set_minute(30);
 
   // First quota: Tuesday, 60 minutes
-  consistency_utils::AddUsageLimitEntryToGoldenInput(&input, TUESDAY, 60,
-                                                     base::nullopt);
+  consistency_utils::AddUsageLimitEntryToGoldenInput(&input, TUESDAY, 60);
   utils::AddTimeUsageLimit(expected_output.get(), utils::kTuesday,
                            base::TimeDelta::FromMinutes(60), kTestLastUpdated);
 
   // Second quota: Friday, 30 minutes
-  consistency_utils::AddUsageLimitEntryToGoldenInput(&input, FRIDAY, 30,
-                                                     base::nullopt);
+  consistency_utils::AddUsageLimitEntryToGoldenInput(&input, FRIDAY, 30);
   utils::AddTimeUsageLimit(expected_output.get(), utils::kFriday,
                            base::TimeDelta::FromMinutes(30), kTestLastUpdated);
 
@@ -119,14 +96,12 @@
       utils::CreateTimeLimitPolicy(base::TimeDelta::FromHours(6));
 
   // First quota: Tuesday, 60 minutes
-  consistency_utils::AddUsageLimitEntryToGoldenInput(&input, TUESDAY, 60,
-                                                     base::nullopt);
+  consistency_utils::AddUsageLimitEntryToGoldenInput(&input, TUESDAY, 60);
   utils::AddTimeUsageLimit(expected_output.get(), utils::kTuesday,
                            base::TimeDelta::FromMinutes(60), kTestLastUpdated);
 
   // Second quota: Friday, 30 minutes
-  consistency_utils::AddUsageLimitEntryToGoldenInput(&input, FRIDAY, 30,
-                                                     base::nullopt);
+  consistency_utils::AddUsageLimitEntryToGoldenInput(&input, FRIDAY, 30);
   utils::AddTimeUsageLimit(expected_output.get(), utils::kFriday,
                            base::TimeDelta::FromMinutes(30), kTestLastUpdated);
 
@@ -136,63 +111,6 @@
   EXPECT_TRUE(*actual_output == *expected_output);
 }
 
-TEST_F(ConsistencyGoldenConverterTest, ConvertInputWithUsageLimitLastUpdated) {
-  ConsistencyGoldenInput input;
-  std::unique_ptr<base::DictionaryValue> expected_output =
-      utils::CreateTimeLimitPolicy(base::TimeDelta::FromHours(6));
-
-  // First quota: Tuesday, 60 minutes
-  consistency_utils::AddUsageLimitEntryToGoldenInput(&input, TUESDAY, 60,
-                                                     kTestTimestamp);
-  utils::AddTimeUsageLimit(expected_output.get(), utils::kTuesday,
-                           base::TimeDelta::FromMinutes(60),
-                           base::Time::FromJavaTime(kTestTimestamp));
-
-  std::unique_ptr<base::DictionaryValue> actual_output =
-      ConvertGoldenInputToProcessorInput(input);
-
-  EXPECT_TRUE(*actual_output == *expected_output);
-}
-
-TEST_F(ConsistencyGoldenConverterTest, ConvertInputWithOverride) {
-  ConsistencyGoldenInput input;
-  std::unique_ptr<base::DictionaryValue> expected_output =
-      utils::CreateTimeLimitPolicy(base::TimeDelta::FromHours(6));
-
-  // Override: Unlock bedtime
-  consistency_utils::AddOverrideToGoldenInput(&input, UNLOCK_WINDOW_LIMIT,
-                                              kTestTimestamp);
-  utils::AddOverride(expected_output.get(),
-                     usage_time_limit::TimeLimitOverride::Action::kUnlock,
-                     base::Time::FromJavaTime(kTestTimestamp));
-
-  std::unique_ptr<base::DictionaryValue> actual_output =
-      ConvertGoldenInputToProcessorInput(input);
-
-  EXPECT_TRUE(*actual_output == *expected_output);
-}
-
-TEST_F(ConsistencyGoldenConverterTest, ConvertInputWithTimedOverride) {
-  ConsistencyGoldenInput input;
-  std::unique_ptr<base::DictionaryValue> expected_output =
-      utils::CreateTimeLimitPolicy(base::TimeDelta::FromHours(6));
-  const int64_t override_duration_millis = 10000;
-
-  // Override: Grant more time
-  consistency_utils::AddTimedOverrideToGoldenInput(
-      &input, override_duration_millis, kTestTimestamp);
-  utils::AddOverrideWithDuration(
-      expected_output.get(),
-      usage_time_limit::TimeLimitOverride::Action::kUnlock,
-      base::Time::FromJavaTime(kTestTimestamp),
-      base::TimeDelta::FromMilliseconds(override_duration_millis));
-
-  std::unique_ptr<base::DictionaryValue> actual_output =
-      ConvertGoldenInputToProcessorInput(input);
-
-  EXPECT_TRUE(*actual_output == *expected_output);
-}
-
 TEST_F(ConsistencyGoldenConverterTest, ConvertOutputWhenUnlocked) {
   usage_time_limit::State state;
   state.is_locked = false;
@@ -257,34 +175,5 @@
   EXPECT_THAT(actual_output, EqualsProto(expected_output));
 }
 
-TEST_F(ConsistencyGoldenConverterTest, GeneratePreviousStateUnlockUsageLimit) {
-  ConsistencyGoldenInput input;
-  consistency_utils::AddOverrideToGoldenInput(&input, UNLOCK_USAGE_LIMIT,
-                                              kTestTimestamp);
-
-  base::Optional<usage_time_limit::State> generated_state =
-      GenerateUnlockUsageLimitOverrideStateFromInput(input);
-
-  EXPECT_TRUE(generated_state->is_locked);
-  EXPECT_TRUE(generated_state->is_time_usage_limit_enabled);
-  EXPECT_EQ(generated_state->active_policy,
-            usage_time_limit::ActivePolicies::kUsageLimit);
-  EXPECT_EQ(generated_state->remaining_usage, base::TimeDelta::FromMinutes(0));
-  EXPECT_EQ(generated_state->time_usage_limit_started,
-            base::Time::FromJavaTime(kTestTimestamp) -
-                base::TimeDelta::FromMinutes(1));
-}
-
-TEST_F(ConsistencyGoldenConverterTest, GeneratePreviousStateOtherOverrides) {
-  ConsistencyGoldenInput input;
-  consistency_utils::AddOverrideToGoldenInput(&input, UNLOCK_WINDOW_LIMIT,
-                                              kTestTimestamp);
-
-  base::Optional<usage_time_limit::State> generated_state =
-      GenerateUnlockUsageLimitOverrideStateFromInput(input);
-
-  EXPECT_EQ(generated_state, base::nullopt);
-}
-
 }  // namespace time_limit_consistency
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test.cc b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test.cc
index ecb2d8a..789c563d 100644
--- a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test.cc
+++ b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test.cc
@@ -34,15 +34,14 @@
       base::Time::FromJavaTime(current_state.time_millis());
   base::Time usage_timestamp =
       base::Time::FromJavaTime(current_state.usage_timestamp());
-  base::Optional<usage_time_limit::State> previous_state =
-      GenerateUnlockUsageLimitOverrideStateFromInput(golden_case.input());
 
   std::unique_ptr<base::DictionaryValue> policy =
       ConvertGoldenInputToProcessorInput(golden_case.input());
   usage_time_limit::State state = usage_time_limit::GetState(
       policy, /* local_override */ nullptr,
       base::TimeDelta::FromMilliseconds(current_state.usage_millis()),
-      usage_timestamp, current_time, timezone, previous_state);
+      usage_timestamp, current_time, timezone,
+      /* previous_state */ base::nullopt);
   ConsistencyGoldenOutput actual_output =
       ConvertProcessorOutputToGoldenOutput(state);
 
diff --git a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test_utils.cc b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test_utils.cc
index ba83796..b45e8886f 100644
--- a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test_utils.cc
+++ b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test_utils.cc
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test_utils.h"
-#include "chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/consistency_golden.pb.h"
+#include "chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_golden_converter.h"
 
 namespace chromeos {
 namespace time_limit_consistency_utils {
@@ -11,57 +10,27 @@
 void AddWindowLimitEntryToGoldenInput(
     time_limit_consistency::ConsistencyGoldenInput* golden_input,
     time_limit_consistency::ConsistencyGoldenEffectiveDay effective_day,
-    const TimeOfDay& starts_at,
-    const TimeOfDay& ends_at,
-    base::Optional<int64_t> last_updated) {
+    int starts_at_hour,
+    int starts_at_minute,
+    int ends_at_hour,
+    int ends_at_minute) {
   time_limit_consistency::ConsistencyGoldenWindowLimitEntry* window =
       golden_input->add_window_limits();
-  window->mutable_starts_at()->set_hour(starts_at.hour);
-  window->mutable_starts_at()->set_minute(starts_at.minute);
-  window->mutable_ends_at()->set_hour(ends_at.hour);
-  window->mutable_ends_at()->set_minute(ends_at.minute);
+  window->mutable_starts_at()->set_hour(starts_at_hour);
+  window->mutable_starts_at()->set_minute(starts_at_minute);
+  window->mutable_ends_at()->set_hour(ends_at_hour);
+  window->mutable_ends_at()->set_minute(ends_at_minute);
   window->set_effective_day(effective_day);
-
-  if (last_updated)
-    window->set_last_updated_millis(last_updated.value());
 }
 
 void AddUsageLimitEntryToGoldenInput(
     time_limit_consistency::ConsistencyGoldenInput* golden_input,
     time_limit_consistency::ConsistencyGoldenEffectiveDay effective_day,
-    int usage_quota_mins,
-    base::Optional<int64_t> last_updated) {
+    int usage_quota_mins) {
   time_limit_consistency::ConsistencyGoldenUsageLimitEntry* usage_limit =
       golden_input->add_usage_limits();
   usage_limit->set_usage_quota_mins(usage_quota_mins);
   usage_limit->set_effective_day(effective_day);
-
-  if (last_updated)
-    usage_limit->set_last_updated_millis(last_updated.value());
-}
-
-void AddOverrideToGoldenInput(
-    time_limit_consistency::ConsistencyGoldenInput* golden_input,
-    time_limit_consistency::ConsistencyGoldenOverrideAction action,
-    int64_t created_at) {
-  DCHECK(action != time_limit_consistency::UNLOCK_UNTIL_LOCK_DEADLINE);
-
-  time_limit_consistency::ConsistencyGoldenOverride* override_entry =
-      golden_input->add_overrides();
-  override_entry->set_action(action);
-  override_entry->set_created_at_millis(created_at);
-}
-
-void AddTimedOverrideToGoldenInput(
-    time_limit_consistency::ConsistencyGoldenInput* golden_input,
-    int64_t duration_millis,
-    int64_t created_at) {
-  time_limit_consistency::ConsistencyGoldenOverride* override_entry =
-      golden_input->add_overrides();
-  override_entry->set_action(
-      time_limit_consistency::UNLOCK_UNTIL_LOCK_DEADLINE);
-  override_entry->set_duration_millis(duration_millis);
-  override_entry->set_created_at_millis(created_at);
 }
 
 }  // namespace time_limit_consistency_utils
diff --git a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test_utils.h b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test_utils.h
index cb16c00..4f6c76d 100644
--- a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test_utils.h
+++ b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/consistency_test_utils.h
@@ -7,48 +7,25 @@
 #ifndef CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_TIME_LIMIT_CONSISTENCY_TEST_CONSISTENCY_TEST_UTILS_H_
 #define CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_TIME_LIMIT_CONSISTENCY_TEST_CONSISTENCY_TEST_UTILS_H_
 
-#include "base/optional.h"
 #include "chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/consistency_golden.pb.h"
 
 namespace chromeos {
 namespace time_limit_consistency_utils {
 
-// A time of day composed of hours and minutes, used when generating bedtime
-// entries.
-struct TimeOfDay {
-  int hour;
-  int minute;
-};
-
 // Adds a time window limit entry to the provided ConsistencyGoldenInput.
 void AddWindowLimitEntryToGoldenInput(
     time_limit_consistency::ConsistencyGoldenInput* golden_input,
     time_limit_consistency::ConsistencyGoldenEffectiveDay effective_day,
-    const TimeOfDay& starts_at,
-    const TimeOfDay& ends_at,
-    base::Optional<int64_t> last_updated);
+    int starts_at_hour,
+    int starts_at_minute,
+    int ends_at_hour,
+    int ends_at_minute);
 
 // Adds a usage limit entry to the provided ConsistencyGoldenInput.
 void AddUsageLimitEntryToGoldenInput(
     time_limit_consistency::ConsistencyGoldenInput* golden_input,
     time_limit_consistency::ConsistencyGoldenEffectiveDay effective_day,
-    int usage_quota_mins,
-    base::Optional<int64_t> last_updated);
-
-// Adds an override to the provided ConsistencyGoldenInput. Must not be used
-// for UNLOCK_UNTIL_LOCK_DEADLINE actions (will DCHECK()), use
-// AddTimedOverrideToGoldenInput() instead.
-void AddOverrideToGoldenInput(
-    time_limit_consistency::ConsistencyGoldenInput* golden_input,
-    time_limit_consistency::ConsistencyGoldenOverrideAction action,
-    int64_t created_at);
-
-// Adds a timed override (UNLOCK_UNTIL_LOCK_DEADLINE action) with duration set
-// to |duration_millis| to the provided ConsistencyGoldenInput.
-void AddTimedOverrideToGoldenInput(
-    time_limit_consistency::ConsistencyGoldenInput* golden_input,
-    int64_t duration_millis,
-    int64_t created_at);
+    int usage_quota_mins);
 
 }  // namespace time_limit_consistency_utils
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/consistency_golden.proto b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/consistency_golden.proto
index f187160..29d87af1 100644
--- a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/consistency_golden.proto
+++ b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/consistency_golden.proto
@@ -23,18 +23,6 @@
   USAGE_LIMIT = 4;
 }
 
-// Actions that an override can take.
-enum ConsistencyGoldenOverrideAction {
-  UNSPECIFIED_ACTION = 0;
-  LOCK = 1;
-  UNLOCK_USAGE_LIMIT = 2;
-  UNLOCK_WINDOW_LIMIT = 3;
-  // Deprecated but kept here for easier conversion to/from the original enum.
-  UNLOCK_LOCK_OVERRIDE = 4 [deprecated = true];
-  UNLOCK_WITH_DEADLINE = 5;
-  UNLOCK_UNTIL_LOCK_DEADLINE = 6;
-}
-
 // Days of the week.
 enum ConsistencyGoldenEffectiveDay {
   UNSPECIFIED_EFFECTIVE_DAY = 0;
@@ -80,9 +68,6 @@
 
   // List of usage time configurations for different days of the week.
   repeated ConsistencyGoldenUsageLimitEntry usage_limits = 3;
-
-  // List of overrides currently active.
-  repeated ConsistencyGoldenOverride overrides = 4;
 }
 
 // Bedtime configuration for a given day.
@@ -95,10 +80,6 @@
 
   // At which hour and minute this bedtime should end. Required
   optional ConsistencyGoldenTimeOfDay ends_at = 3;
-
-  // The moment when this configuration was last updated. Optional but some more
-  // complex scenarios may require it.
-  optional int64 last_updated_millis = 4;
 }
 
 // Usage limit configuration for a given day.
@@ -108,22 +89,6 @@
 
   // Available usage quota for this day in minutes. Required (0 means no quota).
   optional int32 usage_quota_mins = 2;
-
-  // The moment when this configuration was last updated. Optional but some more
-  // complex scenarios may require it.
-  optional int64 last_updated_millis = 3;
-}
-
-// Represents an override that is currently active.
-message ConsistencyGoldenOverride {
-  // What this override does. Required
-  optional ConsistencyGoldenOverrideAction action = 1;
-
-  // Moment when this override was created, in millis. Required.
-  optional int64 created_at_millis = 2;
-
-  // The duration of the unlock, if it is of type "UNLOCK_UNTIL_LOCK_DEADLINE".
-  optional int64 duration_millis = 3;
 }
 
 // Represents a moment of a day.
diff --git a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/override_bedtimes.textproto b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/override_bedtimes.textproto
deleted file mode 100644
index d929032..0000000
--- a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/override_bedtimes.textproto
+++ /dev/null
@@ -1,110 +0,0 @@
-supported_platforms: [ANDROID, CHROME_OS]
-
-# Override created while bedtime is active
-cases {
-  input {
-    window_limits {
-      effective_day: MONDAY
-      starts_at {
-        hour: 23
-        minute: 0
-      }
-      ends_at {
-        hour: 8
-        minute: 0
-      }
-    }
-    overrides {
-      action: UNLOCK_WINDOW_LIMIT
-      # Monday, Jan 28th 2019 at 11:29pm
-      created_at_millis: 1548718140000
-    }
-  }
-  current_state {
-    # Monday, Jan 28th 2019 at 11:30pm
-    time_millis: 1548718200000
-    timezone: "GMT"
-  }
-  output {
-    is_locked: false
-    active_policy: OVERRIDE
-    next_active_policy: FIXED_LIMIT
-  }
-}
-
-# Override created before bedtime started
-cases {
-  input {
-    window_limits {
-      effective_day: MONDAY
-      starts_at {
-        hour: 23
-        minute: 0
-      }
-      ends_at {
-        hour: 8
-        minute: 0
-      }
-    }
-    overrides {
-      action: UNLOCK_WINDOW_LIMIT
-      # Monday, Jan 28th 2019 at 10:30pm
-      created_at_millis: 1548714600000
-    }
-  }
-  current_state {
-    # Monday, Jan 28th 2019 at 11:30pm
-    time_millis: 1548718200000
-    timezone: "GMT"
-  }
-  output {
-    is_locked: true
-    active_policy: FIXED_LIMIT
-    # Tuesday, Jan 29th 2019 at 8:00am
-    next_unlocking_time_millis: 1548748800000
-    next_active_policy: NO_ACTIVE_POLICY
-  }
-}
-
-# Override created during intersection of bedtimes
-cases {
-  input {
-    window_limits {
-      effective_day: MONDAY
-      starts_at {
-        hour: 23
-        minute: 0
-      }
-      ends_at {
-        hour: 8
-        minute: 0
-      }
-    }
-    window_limits {
-      effective_day: TUESDAY
-      starts_at {
-        hour: 2
-        minute: 0
-      }
-      ends_at {
-        hour: 11
-        minute: 0
-      }
-    }
-    overrides {
-      action: UNLOCK_WINDOW_LIMIT
-      # Tuesday, Jan 29th 2019 at 2:20am
-      created_at_millis: 1548728400000
-    }
-  }
-  current_state {
-    # Tuesday, Jan 29th 2019 at 2:30am
-    time_millis: 1548729000000
-    timezone: "GMT"
-  }
-  output {
-    is_locked: false
-    active_policy: OVERRIDE
-    next_active_policy: FIXED_LIMIT
-  }
-}
diff --git a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/override_then_modify.textproto b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/override_then_modify.textproto
deleted file mode 100644
index 515e1db5..0000000
--- a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/override_then_modify.textproto
+++ /dev/null
@@ -1,102 +0,0 @@
-supported_platforms: [ANDROID, CHROME_OS]
-
-# Override created for bedtime, then bedtime is altered.
-cases {
-  input {
-    window_limits {
-      effective_day: MONDAY
-      starts_at {
-        hour: 23
-        minute: 0
-      }
-      ends_at {
-        hour: 8
-        minute: 0
-      }
-      # Monday, Jan 28th 2019 at 11:25pm
-      last_updated_millis: 1548717900000
-    }
-    overrides {
-      action: UNLOCK_WINDOW_LIMIT
-      # Monday, Jan 28th 2019 at 11:20pm
-      created_at_millis: 1548717600000
-    }
-  }
-  current_state {
-    # Monday, Jan 28th 2019 at 11:30pm
-    time_millis: 1548718200000
-    timezone: "GMT"
-  }
-  output {
-    is_locked: true
-    active_policy: FIXED_LIMIT
-    next_active_policy: NO_ACTIVE_POLICY
-    # Tuesday, Jan 29th 2019 at 8:00am
-    next_unlocking_time_millis: 1548748800000
-  }
-}
-
-# Override created after hitting usage limit, then usage limit quota is updated
-# to a value bigger than the current usage.
-cases {
-  input {
-    usage_limits {
-      effective_day: THURSDAY
-      usage_quota_mins: 120
-      # Thursday, Feb 21st 2019 at 2:50pm
-      last_updated_millis: 1550760600000
-    }
-    overrides {
-      action: UNLOCK_USAGE_LIMIT
-      # Thursday, Feb 21st 2019 at 2:40pm
-      created_at_millis: 1550760000000
-    }
-  }
-  current_state {
-    # Thursday, Feb 21st 2019 at 3:00pm
-    time_millis: 1550761200000
-    timezone: "GMT"
-    # 1 hour
-    usage_millis: 3600000
-  }
-  output {
-    is_locked: false
-    active_policy: NO_ACTIVE_POLICY
-    next_active_policy: USAGE_LIMIT
-    # 1 hour
-    remaining_quota_millis: 3600000
-  }
-}
-
-# Override created after hitting usage limit, then usage limit quota is updated
-# to a value smaller than the current usage.
-cases {
-  input {
-    usage_limits {
-      effective_day: THURSDAY
-      usage_quota_mins: 45
-      # Thursday, Feb 21st 2019 at 2:50pm
-      last_updated_millis: 1550760600000
-    }
-    overrides {
-      action: UNLOCK_USAGE_LIMIT
-      # Thursday, Feb 21st 2019 at 2:40pm
-      created_at_millis: 1550760000000
-    }
-  }
-  current_state {
-    # Thursday, Feb 21st 2019 at 3:00pm
-    time_millis: 1550761200000
-    timezone: "GMT"
-    # 1 hour
-    usage_millis: 3600000
-  }
-  output {
-    is_locked: true
-    active_policy: USAGE_LIMIT
-    # Friday, Feb 22nd 2019 at 6:00am
-    next_unlocking_time_millis: 1550815200000
-    next_active_policy: NO_ACTIVE_POLICY
-    remaining_quota_millis: 0
-  }
-}
diff --git a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/override_usage_limits_android.textproto b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/override_usage_limits_android.textproto
deleted file mode 100644
index 77ce4298..0000000
--- a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/override_usage_limits_android.textproto
+++ /dev/null
@@ -1,61 +0,0 @@
-# There is a different behavior on Android and ChromeOS for the first test case
-# so they are split in two different suites temporarily.
-
-supported_platforms: [ANDROID]
-
-# Override created while usage limit is active
-cases {
-  input {
-    usage_limits {
-      effective_day: THURSDAY
-      usage_quota_mins: 120
-    }
-    overrides {
-      action: UNLOCK_USAGE_LIMIT
-      # Thursday, Feb 21th 2019 at 14:50pm
-      created_at_millis: 1550760600000
-    }
-  }
-  current_state {
-    # Thursday, Feb 21th 2019 at 15:00pm
-    time_millis: 1550761200000
-    timezone: "GMT"
-    # 2 hours
-    usage_millis: 7200000
-  }
-  output {
-    is_locked: false
-    active_policy: OVERRIDE
-    next_active_policy: USAGE_LIMIT
-  }
-}
-
-# Usage limit reached while other override is in place
-cases {
-  input {
-    usage_limits {
-      effective_day: THURSDAY
-      usage_quota_mins: 120
-    }
-    overrides {
-      action: UNLOCK_WINDOW_LIMIT
-      # Thursday, Feb 21th 2019 at 14:50pm
-      created_at_millis: 1550760600000
-    }
-  }
-  current_state {
-    # Thursday, Feb 21th 2019 at 15:00pm
-    time_millis: 1550761200000
-    timezone: "GMT"
-    # 2 hours
-    usage_millis: 7200000
-  }
-  output {
-    is_locked: true
-    active_policy: USAGE_LIMIT
-    next_active_policy: NO_ACTIVE_POLICY
-    remaining_quota_millis: 0
-    # Friday, Feb 22nd 2019 at 6:00am
-    next_unlocking_time_millis: 1550815200000
-  }
-}
diff --git a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/override_usage_limits_cros.textproto b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/override_usage_limits_cros.textproto
deleted file mode 100644
index e123418d..0000000
--- a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/override_usage_limits_cros.textproto
+++ /dev/null
@@ -1,61 +0,0 @@
-# There is a different behavior on Android and ChromeOS for the first test case
-# so they are split in two different suites temporarily.
-
-supported_platforms: [CHROME_OS]
-
-# Override created while usage limit is active
-cases {
-  input {
-    usage_limits {
-      effective_day: THURSDAY
-      usage_quota_mins: 120
-    }
-    overrides {
-      action: UNLOCK_USAGE_LIMIT
-      # Thursday, Feb 21th 2019 at 14:50pm
-      created_at_millis: 1550760600000
-    }
-  }
-  current_state {
-    # Thursday, Feb 21th 2019 at 15:00pm
-    time_millis: 1550761200000
-    timezone: "GMT"
-    # 2 hours
-    usage_millis: 7200000
-  }
-  output {
-    is_locked: false
-    active_policy: OVERRIDE
-    next_active_policy: NO_ACTIVE_POLICY
-  }
-}
-
-# Usage limit reached while other override is in place
-cases {
-  input {
-    usage_limits {
-      effective_day: THURSDAY
-      usage_quota_mins: 120
-    }
-    overrides {
-      action: UNLOCK_WINDOW_LIMIT
-      # Thursday, Feb 21th 2019 at 14:50pm
-      created_at_millis: 1550760600000
-    }
-  }
-  current_state {
-    # Thursday, Feb 21th 2019 at 15:00pm
-    time_millis: 1550761200000
-    timezone: "GMT"
-    # 2 hours
-    usage_millis: 7200000
-  }
-  output {
-    is_locked: true
-    active_policy: USAGE_LIMIT
-    next_active_policy: NO_ACTIVE_POLICY
-    remaining_quota_millis: 0
-    # Friday, Feb 22nd 2019 at 6:00am
-    next_unlocking_time_millis: 1550815200000
-  }
-}
diff --git a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/simple_override.textproto b/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/simple_override.textproto
deleted file mode 100644
index db0ceed..0000000
--- a/chrome/browser/chromeos/child_accounts/time_limit_consistency_test/goldens/simple_override.textproto
+++ /dev/null
@@ -1,24 +0,0 @@
-supported_platforms: [ANDROID, CHROME_OS]
-
-# Create a simple lock override.
-cases {
-  input {
-    overrides {
-      action: LOCK
-      # Thursday, Feb 21st 2019 at 14:50pm
-      created_at_millis: 1550760600000
-    }
-  }
-  current_state {
-    # Thursday, Feb 21st 2019 at 15:00pm
-    time_millis: 1550761200000
-    timezone: "GMT"
-  }
-  output {
-    is_locked: true
-    active_policy: OVERRIDE
-    # Friday, Feb 22nd 2019 at 6:00am
-    next_unlocking_time_millis: 1550815200000
-    next_active_policy: NO_ACTIVE_POLICY
-  }
-}
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index d0ddfbc..967c4a5aa 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -1043,8 +1043,7 @@
       std::make_unique<power::auto_screen_brightness::Controller>();
 
   // Enable Chrome OS USB detection only if a USB feature is turned on.
-  // Other USB features should also be checked here when they are added.
-  if (base::FeatureList::IsEnabled(chromeos::features::kCrostiniUsbSupport)) {
+  if (base::FeatureList::IsEnabled(features::kCrostiniUsbSupport)) {
     cros_usb_detector_ = std::make_unique<CrosUsbDetector>();
     cros_usb_detector_->ConnectToDeviceManager();
   }
@@ -1094,6 +1093,10 @@
   // BrowserPolicyConnector (owned by g_browser_process).
   DeviceSettingsService::Get()->UnsetSessionManager();
 
+  // Destroy the CrosUsb detector so it stops trying to reconnect to the
+  // UsbDeviceManager
+  cros_usb_detector_.reset();
+
   // We should remove observers attached to D-Bus clients before
   // DBusThreadManager is shut down.
   network_pref_state_observer_.reset();
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index cffc74b3..2d3e3b8 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -28,6 +28,7 @@
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/usb/cros_usb_detector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/window_properties.h"
@@ -599,12 +600,10 @@
 CrostiniManager::CrostiniManager(Profile* profile)
     : profile_(profile),
       owner_id_(CryptohomeIdForProfile(profile)),
-      binding_(this),
       weak_ptr_factory_(this) {
   DCHECK(!profile_->IsOffTheRecord());
   GetCiceroneClient()->AddObserver(this);
   GetConciergeClient()->AddObserver(this);
-  InitializeUsbDeviceManager();
 }
 
 CrostiniManager::~CrostiniManager() {
@@ -1359,14 +1358,14 @@
 }
 
 void CrostiniManager::SetInstallerViewStatus(bool open) {
-  installer_view_status_ = open;
+  installer_dialog_showing_ = open;
   for (auto& observer : installer_view_status_observers_) {
     observer.OnCrostiniInstallerViewStatusChanged(open);
   }
 }
 
 bool CrostiniManager::GetInstallerViewStatus() const {
-  return installer_view_status_;
+  return installer_dialog_showing_;
 }
 
 void CrostiniManager::AddInstallerViewStatusObserver(
@@ -1386,26 +1385,8 @@
 
 void CrostiniManager::AttachUsbDevice(const std::string& vm_name,
                                       device::mojom::UsbDeviceInfoPtr device,
+                                      base::ScopedFD fd,
                                       AttachUsbDeviceCallback callback) {
-  usb_manager_->OpenFileDescriptor(
-      device->guid,
-      base::BindOnce(&CrostiniManager::OnUsbDeviceOpened,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
-                     std::move(device), vm_name));
-}
-
-void CrostiniManager::OnUsbDeviceOpened(AttachUsbDeviceCallback callback,
-                                        device::mojom::UsbDeviceInfoPtr device,
-                                        const std::string& vm_name,
-                                        base::File file) {
-  if (!file.IsValid()) {
-    LOG(ERROR) << "Permission broker refused to allow access to USB device";
-    std::move(callback).Run(CrostiniResult::PERMISSION_BROKER_ERROR);
-    return;
-  }
-
-  base::ScopedFD fd(file.TakePlatformFile());
-
   vm_tools::concierge::AttachUsbDeviceRequest request;
   request.set_vm_name(vm_name);
   request.set_owner_id(CryptohomeIdForProfile(profile_));
@@ -1429,36 +1410,23 @@
   if (reply.has_value()) {
     vm_tools::concierge::AttachUsbDeviceResponse response = reply.value();
     if (response.success()) {
-      attached_usb_devices_.emplace(
-          device->guid, std::make_pair(vm_name, response.guest_port()));
-      attached_usb_devices_reverse_.emplace(
-          std::make_pair(vm_name, response.guest_port()), device->guid);
-
-      std::move(callback).Run(CrostiniResult::SUCCESS);
+      std::move(callback).Run(response.guest_port(), CrostiniResult::SUCCESS);
     } else {
       LOG(ERROR) << "Failed to attach USB device, " << response.reason();
-      std::move(callback).Run(CrostiniResult::ATTACH_USB_FAILED);
+      std::move(callback).Run(chromeos::kInvalidUsbPortNumber,
+                              CrostiniResult::ATTACH_USB_FAILED);
     }
   } else {
     LOG(ERROR) << "Failed to attach USB device, empty dbus response";
-    std::move(callback).Run(CrostiniResult::DBUS_ERROR);
+    std::move(callback).Run(chromeos::kInvalidUsbPortNumber,
+                            CrostiniResult::DBUS_ERROR);
   }
 }
 
 void CrostiniManager::DetachUsbDevice(const std::string& vm_name,
                                       device::mojom::UsbDeviceInfoPtr device,
+                                      uint8_t guest_port,
                                       DetachUsbDeviceCallback callback) {
-  auto it = attached_usb_devices_.find(device->guid);
-  uint8_t guest_port = 0;
-  if (it != attached_usb_devices_.end() && it->second.first == vm_name) {
-    guest_port = it->second.second;
-  } else {
-    // If there wasn't an existing attachment, then removal is a no-op and
-    // always succeeds
-    std::move(callback).Run(CrostiniResult::SUCCESS);
-    return;
-  }
-
   vm_tools::concierge::DetachUsbDeviceRequest request;
   request.set_vm_name(vm_name);
   request.set_owner_id(CryptohomeIdForProfile(profile_));
@@ -1480,8 +1448,6 @@
   if (reply.has_value()) {
     vm_tools::concierge::DetachUsbDeviceResponse response = reply.value();
     if (response.success()) {
-      attached_usb_devices_.erase(device->guid);
-      attached_usb_devices_reverse_.erase(std::make_pair(vm_name, guest_port));
       std::move(callback).Run(CrostiniResult::SUCCESS);
     } else {
       LOG(ERROR) << "Failed to detach USB device, " << response.reason();
@@ -1493,51 +1459,6 @@
   }
 }
 
-void CrostiniManager::OnDeviceAdded(
-    device::mojom::UsbDeviceInfoPtr device_info) {}
-
-void CrostiniManager::OnDeviceRemoved(
-    device::mojom::UsbDeviceInfoPtr device_info) {
-  auto it = attached_usb_devices_.find(device_info->guid);
-  if (it != attached_usb_devices_.end()) {
-    DetachUsbDevice(it->second.first, std::move(device_info),
-                    base::DoNothing());
-  }
-}
-
-void CrostiniManager::SetUsbManagerForTesting(
-    device::mojom::UsbDeviceManagerPtr usb_manager) {
-  DCHECK(!usb_manager_);
-  usb_manager_ = std::move(usb_manager);
-  InitializeUsbDeviceManagerClient();
-}
-
-void CrostiniManager::InitializeUsbDeviceManager() {
-  auto* connection = content::ServiceManagerConnection::GetForProcess();
-  if (connection) {
-    connection->GetConnector()->BindInterface(device::mojom::kServiceName,
-                                              mojo::MakeRequest(&usb_manager_));
-    usb_manager_.set_connection_error_handler(
-        base::BindOnce(&CrostiniManager::InitializeUsbDeviceManager,
-                       weak_ptr_factory_.GetWeakPtr()));
-    InitializeUsbDeviceManagerClient();
-  } else {
-    LOG(ERROR) << "ServiceManagerConnection not found";
-  }
-}
-
-void CrostiniManager::InitializeUsbDeviceManagerClient() {
-  device::mojom::UsbDeviceManagerClientAssociatedPtrInfo ptr;
-  auto request = mojo::MakeRequest(&ptr);
-
-  usb_manager_->SetClient(std::move(ptr));
-
-  if (binding_.is_bound()) {
-    binding_.Close();
-  }
-  binding_.Bind(std::move(request));
-}
-
 void CrostiniManager::ListUsbDevices(const std::string& vm_name,
                                      ListUsbDevicesCallback callback) {
   vm_tools::concierge::ListUsbDeviceRequest request;
@@ -1557,54 +1478,21 @@
   if (reply.has_value()) {
     vm_tools::concierge::ListUsbDeviceResponse response = reply.value();
     if (response.success()) {
-      usb_manager_->GetDevices(
-          nullptr, base::BindOnce(&CrostiniManager::OnListUsbDeviceInfoPtrs,
-                                  weak_ptr_factory_.GetWeakPtr(), vm_name,
-                                  std::move(response), std::move(callback)));
+      std::vector<std::pair<std::string, uint8_t>> mount_points;
+      for (const auto& dev : response.usb_devices()) {
+        mount_points.push_back(std::make_pair(vm_name, dev.guest_port()));
+      }
+      std::move(callback).Run(CrostiniResult::SUCCESS, std::move(mount_points));
     } else {
       LOG(ERROR) << "Failed to list USB devices";
-      std::move(callback).Run(CrostiniResult::LIST_USB_FAILED,
-                              std::vector<device::mojom::UsbDeviceInfoPtr>());
+      std::move(callback).Run(CrostiniResult::LIST_USB_FAILED, {});
     }
   } else {
     LOG(ERROR) << "Failed to list USB devices, empty dbus response";
-    std::move(callback).Run(CrostiniResult::DBUS_ERROR,
-                            std::vector<device::mojom::UsbDeviceInfoPtr>());
+    std::move(callback).Run(CrostiniResult::DBUS_ERROR, {});
   }
 }
 
-void CrostiniManager::OnListUsbDeviceInfoPtrs(
-    const std::string& vm_name,
-    vm_tools::concierge::ListUsbDeviceResponse response,
-    ListUsbDevicesCallback callback,
-    std::vector<device::mojom::UsbDeviceInfoPtr> device_info) {
-  auto result = CrostiniResult::SUCCESS;
-  std::set<std::string> guids;
-  for (auto dev : response.usb_devices()) {
-    auto key = std::make_pair(vm_name, dev.guest_port());
-    auto iter = attached_usb_devices_reverse_.find(key);
-    if (iter != attached_usb_devices_reverse_.end()) {
-      guids.insert(iter->second);
-    } else {
-      // This shouldn't happen normally, but could as a result of a user
-      // manually adding a USB device from the commandline. In this case, we
-      // make a best effort and return the UsbDeviceInfoPtr objects we do know
-      // about and ignore the rest. In the future, we may be able to update
-      // our internal registry and handle this case properly.
-      result = CrostiniResult::UNKNOWN_USB_DEVICE;
-    }
-  }
-
-  std::vector<device::mojom::UsbDeviceInfoPtr> filtered_devices;
-  for (auto& dev : device_info) {
-    if (guids.find(dev->guid) != guids.end()) {
-      filtered_devices.push_back(std::move(dev));
-    }
-  }
-
-  std::move(callback).Run(result, std::move(filtered_devices));
-}
-
 // static
 GURL CrostiniManager::GenerateVshInCroshUrl(
     Profile* profile,
@@ -2562,6 +2450,11 @@
   for (const auto& pending_restarter : pending_restarters) {
     pending_restarter->RunCallback(result);
   }
+
+  if (chromeos::CrosUsbDetector::Get()) {
+    // Mount shared devices
+    chromeos::CrosUsbDetector::Get()->ConnectSharedDevicesOnVmStartup();
+  }
 }
 
 void CrostiniManager::OnSearchApp(
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.h b/chrome/browser/chromeos/crostini/crostini_manager.h
index 4c555e1..8ef3453 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.h
+++ b/chrome/browser/chromeos/crostini/crostini_manager.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_MANAGER_H_
 
 #include <map>
-#include <set>
 #include <string>
 #include <utility>
 #include <vector>
@@ -26,8 +25,6 @@
 #include "chromeos/dbus/concierge_client.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "device/usb/public/mojom/device_manager.mojom.h"
-#include "device/usb/public/mojom/device_manager_client.mojom.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
 
 class Profile;
 
@@ -206,8 +203,7 @@
 // only the Concierge name is exposed outside of here.
 class CrostiniManager : public KeyedService,
                         public chromeos::ConciergeClient::Observer,
-                        public chromeos::CiceroneClient::Observer,
-                        public device::mojom::UsbDeviceManagerClient {
+                        public chromeos::CiceroneClient::Observer {
  public:
   using CrostiniResultCallback =
       base::OnceCallback<void(CrostiniResult result)>;
@@ -259,13 +255,16 @@
   // The type of the callback for CrostiniManager::RemoveCrostini.
   using RemoveCrostiniCallback = CrostiniResultCallback;
   // The type of the callback for CrostiniManager::AttachUsbDevice
-  using AttachUsbDeviceCallback = CrostiniResultCallback;
+  // Note: The guest_port is only valid when the result is ::SUCCESS.
+  using AttachUsbDeviceCallback =
+      base::OnceCallback<void(uint8_t guest_port, CrostiniResult result)>;
   // The type of the callback for CrostiniManager::DetachUsbDevice
   using DetachUsbDeviceCallback = CrostiniResultCallback;
   // The type of the callback for CrostiniManager::ListUsbDevices
   using ListUsbDevicesCallback = base::OnceCallback<void(
       CrostiniResult result,
-      std::vector<device::mojom::UsbDeviceInfoPtr> devices)>;
+      std::vector<std::pair<std::string, uint8_t>> devices)>;
+
   // The type of the callback for CrostiniManager::SearchApp.
   using SearchAppCallback =
       base::OnceCallback<void(const std::vector<std::string>& package_names)>;
@@ -486,14 +485,23 @@
                            std::string container_name,
                            GetContainerSshKeysCallback callback);
 
+  // Called when a USB device should be attached into the VM.
+  // Should only ever be called on user action.
   void AttachUsbDevice(const std::string& vm_name,
                        device::mojom::UsbDeviceInfoPtr device,
+                       base::ScopedFD fd,
                        AttachUsbDeviceCallback callback);
 
+  // Called when a USB device should be detached from the VM.
+  // May be called on user action or on USB removal.
   void DetachUsbDevice(const std::string& vm_name,
                        device::mojom::UsbDeviceInfoPtr device,
+                       uint8_t guest_port,
                        DetachUsbDeviceCallback callback);
 
+  // Lists USB devices attached to a guest VM.
+  // TODO(jopra): Rename to reflect that this now lists the mount points for USB
+  // devices.
   void ListUsbDevices(const std::string& vm_name,
                       ListUsbDevicesCallback callback);
 
@@ -597,8 +605,7 @@
       const vm_tools::cicerone::ImportLxdContainerProgressSignal& signal)
       override;
 
-  void RemoveCrostini(std::string vm_name,
-                      RemoveCrostiniCallback callback);
+  void RemoveCrostini(std::string vm_name, RemoveCrostiniCallback callback);
 
   void SetVmState(std::string vm_name, VmState vm_state);
   bool IsVmRunning(std::string vm_name);
@@ -626,12 +633,6 @@
     component_manager_load_error_for_testing_ = error;
   }
 
-  // device::mojom::UsbDeviceManagerClient::
-  void OnDeviceAdded(device::mojom::UsbDeviceInfoPtr device_info) override;
-  void OnDeviceRemoved(device::mojom::UsbDeviceInfoPtr device_info) override;
-
-  void SetUsbManagerForTesting(device::mojom::UsbDeviceManagerPtr usb_manager);
-
   void SetInstallerViewStatus(bool open);
   bool GetInstallerViewStatus() const;
   void AddInstallerViewStatusObserver(InstallerViewStatusObserver* observer);
@@ -774,11 +775,6 @@
       GetContainerSshKeysCallback callback,
       base::Optional<vm_tools::concierge::ContainerSshKeysResponse> reply);
 
-  void OnUsbDeviceOpened(AttachUsbDeviceCallback callback,
-                         device::mojom::UsbDeviceInfoPtr device,
-                         const std::string& vm_name,
-                         base::File file);
-
   // Callback for CrostiniManager::OnAttachUsbDeviceOpen
   void OnAttachUsbDevice(
       const std::string& vm_name,
@@ -819,7 +815,6 @@
   // checking component registration code may block.
   void MaybeUpgradeCrostiniAfterChecks();
 
-
   void FinishRestart(CrostiniRestarter* restarter, CrostiniResult result);
 
   // Callback for CrostiniManager::AbortRestartCrostini
@@ -829,9 +824,6 @@
   // Callback for CrostiniManager::RemoveCrostini.
   void OnRemoveCrostini(CrostiniResult result);
 
-  void InitializeUsbDeviceManager();
-  void InitializeUsbDeviceManagerClient();
-
   Profile* profile_;
   std::string owner_id_;
 
@@ -910,19 +902,10 @@
   std::map<CrostiniManager::RestartId, scoped_refptr<CrostiniRestarter>>
       restarters_by_id_;
 
-  // A mapping from GUID -> (VM name, guest port) for each attached USB device
-  std::map<std::string, std::pair<std::string, uint8_t>> attached_usb_devices_;
-  // A mapping from (VM name, guest port) -> GUID for each attached USB device
-  std::map<std::pair<std::string, uint8_t>, std::string>
-      attached_usb_devices_reverse_;
-
-  mojo::AssociatedBinding<device::mojom::UsbDeviceManagerClient> binding_;
-
-  device::mojom::UsbDeviceManagerPtr usb_manager_;
-
   // True when the installer dialog is showing. At that point, it is invalid
   // to allow Crostini uninstallation.
-  bool installer_view_status_ = false;
+  bool installer_dialog_showing_ = false;
+
   base::ObserverList<InstallerViewStatusObserver>
       installer_view_status_observers_;
 
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
index d5ab34f..5cede0b 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
@@ -32,6 +32,7 @@
 const char kContainerName[] = "container_name";
 const char kPackageID[] = "package;1;;";
 constexpr int64_t kDiskSizeBytes = 4ll * 1024 * 1024 * 1024;  // 4 GiB
+const uint8_t kUsbPort = 0x01;
 }  // namespace
 
 class CrostiniManagerTest : public testing::Test {
@@ -131,8 +132,16 @@
     std::move(closure).Run();
   }
 
+  base::ScopedFD TestFileDescriptor() {
+    base::File file(base::FilePath("/dev/null"),
+                    base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+    base::ScopedFD fd(file.TakePlatformFile());
+    return fd;
+  }
+
   void AttachUsbDeviceCallback(base::OnceClosure closure,
                                CrostiniResult expected_result,
+                               uint8_t guest_port,
                                CrostiniResult result) {
     EXPECT_TRUE(fake_concierge_client_->attach_usb_device_called());
     EXPECT_EQ(expected_result, result);
@@ -154,7 +163,7 @@
       CrostiniResult expected_result,
       size_t expected_size,
       CrostiniResult result,
-      std::vector<device::mojom::UsbDeviceInfoPtr> devices) {
+      std::vector<std::pair<std::string, uint8_t>> devices) {
     EXPECT_TRUE(fake_concierge_client_->list_usb_devices_called());
     EXPECT_EQ(expected_result, result);
     EXPECT_EQ(devices.size(), expected_size);
@@ -211,8 +220,6 @@
 
     device::mojom::UsbDeviceManagerPtr fake_usb_manager_ptr_;
     fake_usb_manager_.AddBinding(mojo::MakeRequest(&fake_usb_manager_ptr_));
-    crostini_manager_->SetUsbManagerForTesting(
-        std::move(fake_usb_manager_ptr_));
   }
 
   void TearDown() override {
@@ -487,7 +494,7 @@
   auto guid = fake_usb->guid;
 
   crostini_manager()->AttachUsbDevice(
-      kVmName, std::move(fake_usb),
+      kVmName, std::move(fake_usb), TestFileDescriptor(),
       base::BindOnce(&CrostiniManagerTest::AttachUsbDeviceCallback,
                      base::Unretained(this), run_loop()->QuitClosure(),
                      CrostiniResult::SUCCESS));
@@ -504,7 +511,7 @@
   auto guid = fake_usb->guid;
 
   crostini_manager()->AttachUsbDevice(
-      kVmName, std::move(fake_usb),
+      kVmName, std::move(fake_usb), TestFileDescriptor(),
       base::BindOnce(&CrostiniManagerTest::AttachUsbDeviceCallback,
                      base::Unretained(this), run_loop()->QuitClosure(),
                      CrostiniResult::ATTACH_USB_FAILED));
@@ -512,19 +519,6 @@
   fake_usb_manager_.RemoveDevice(guid);
 }
 
-TEST_F(CrostiniManagerTest, DetachUsbDeviceNoop) {
-  auto fake_usb = fake_usb_manager_.CreateAndAddDevice(0, 0);
-  auto guid = fake_usb->guid;
-
-  crostini_manager()->DetachUsbDevice(
-      kVmName, std::move(fake_usb),
-      base::BindOnce(&CrostiniManagerTest::DetachUsbDeviceCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(), false,
-                     CrostiniResult::SUCCESS));
-  run_loop()->Run();
-  fake_usb_manager_.RemoveDevice(guid);
-}
-
 TEST_F(CrostiniManagerTest, DetachUsbDeviceSuccess) {
   vm_tools::concierge::AttachUsbDeviceResponse attach_response;
   attach_response.set_success(true);
@@ -539,13 +533,13 @@
 
   auto detach_usb = base::BindOnce(
       &CrostiniManager::DetachUsbDevice, base::Unretained(crostini_manager()),
-      kVmName, fake_usb.Clone(),
+      kVmName, fake_usb.Clone(), kUsbPort,
       base::BindOnce(&CrostiniManagerTest::DetachUsbDeviceCallback,
                      base::Unretained(this), run_loop()->QuitClosure(), true,
                      CrostiniResult::SUCCESS));
 
   crostini_manager()->AttachUsbDevice(
-      kVmName, std::move(fake_usb),
+      kVmName, std::move(fake_usb), TestFileDescriptor(),
       base::BindOnce(&CrostiniManagerTest::AttachUsbDeviceCallback,
                      base::Unretained(this), std::move(detach_usb),
                      CrostiniResult::SUCCESS));
@@ -567,13 +561,13 @@
 
   auto detach_usb = base::BindOnce(
       &CrostiniManager::DetachUsbDevice, base::Unretained(crostini_manager()),
-      kVmName, fake_usb.Clone(),
+      kVmName, fake_usb.Clone(), kUsbPort,
       base::BindOnce(&CrostiniManagerTest::DetachUsbDeviceCallback,
                      base::Unretained(this), run_loop()->QuitClosure(), true,
                      CrostiniResult::DETACH_USB_FAILED));
 
   crostini_manager()->AttachUsbDevice(
-      kVmName, std::move(fake_usb),
+      kVmName, std::move(fake_usb), TestFileDescriptor(),
       base::BindOnce(&CrostiniManagerTest::AttachUsbDeviceCallback,
                      base::Unretained(this), std::move(detach_usb),
                      CrostiniResult::SUCCESS));
@@ -615,7 +609,7 @@
   auto guid = fake_usb->guid;
 
   crostini_manager()->AttachUsbDevice(
-      kVmName, std::move(fake_usb),
+      kVmName, std::move(fake_usb), TestFileDescriptor(),
       base::BindOnce(&CrostiniManagerTest::AttachUsbDeviceCallback,
                      base::Unretained(this), run_loop()->QuitClosure(),
                      CrostiniResult::SUCCESS));
@@ -637,40 +631,6 @@
   fake_usb_manager_.RemoveDevice(guid);
 }
 
-TEST_F(CrostiniManagerTest, ListUsbDeviceExtra) {
-  vm_tools::concierge::AttachUsbDeviceResponse attach_response;
-  attach_response.set_success(true);
-  attach_response.set_guest_port(1);
-  fake_concierge_client_->set_attach_usb_device_response(attach_response);
-
-  auto fake_usb = fake_usb_manager_.CreateAndAddDevice(0, 0);
-  auto guid = fake_usb->guid;
-
-  crostini_manager()->AttachUsbDevice(
-      kVmName, std::move(fake_usb),
-      base::BindOnce(&CrostiniManagerTest::AttachUsbDeviceCallback,
-                     base::Unretained(this), run_loop()->QuitClosure(),
-                     CrostiniResult::SUCCESS));
-  run_loop()->Run();
-
-  vm_tools::concierge::ListUsbDeviceResponse response;
-  response.set_success(true);
-  auto* msg = response.add_usb_devices();
-  msg->set_guest_port(1);
-  msg = response.add_usb_devices();
-  msg->set_guest_port(2);
-  fake_concierge_client_->set_list_usb_devices_response(response);
-
-  base::RunLoop run_loop2;
-  crostini_manager()->ListUsbDevices(
-      kVmName, base::BindOnce(&CrostiniManagerTest::ListUsbDevicesCallback,
-                              base::Unretained(this), run_loop2.QuitClosure(),
-                              CrostiniResult::UNKNOWN_USB_DEVICE, 1));
-  run_loop2.Run();
-
-  fake_usb_manager_.RemoveDevice(guid);
-}
-
 class CrostiniManagerRestartTest : public CrostiniManagerTest,
                                    public CrostiniManager::RestartObserver {
  public:
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 212bf8b0..0e5dbf0 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -179,12 +179,14 @@
 std::string SetWhitelistedPref(Profile* profile,
                                const std::string& pref_name,
                                const base::Value& value) {
-  if (pref_name == arc::prefs::kVoiceInteractionHotwordEnabled) {
+  if (pref_name == arc::prefs::kVoiceInteractionEnabled ||
+      pref_name == arc::prefs::kVoiceInteractionHotwordEnabled) {
     DCHECK(value.is_bool());
-
-    if (assistant::IsAssistantAllowedForProfile(profile) !=
-        ash::mojom::AssistantAllowedState::ALLOWED) {
-      return "Assistant is not available for the current user";
+    ash::mojom::AssistantAllowedState allowed_state =
+        assistant::IsAssistantAllowedForProfile(profile);
+    if (allowed_state != ash::mojom::AssistantAllowedState::ALLOWED) {
+      return base::StringPrintf("Assistant not allowed - state: %d",
+                                allowed_state);
     }
   } else if (pref_name == ash::prefs::kAccessibilityVirtualKeyboardEnabled) {
     DCHECK(value.is_bool());
@@ -1302,13 +1304,12 @@
   EXTENSION_FUNCTION_VALIDATE(params);
 
   Profile* profile = Profile::FromBrowserContext(browser_context());
-  if (assistant::IsAssistantAllowedForProfile(profile) !=
-      ash::mojom::AssistantAllowedState::ALLOWED) {
-    return RespondNow(Error("Assistant is not available for the current user"));
-  }
+  const std::string& err_msg =
+      SetWhitelistedPref(profile, arc::prefs::kVoiceInteractionEnabled,
+                         base::Value(params->enabled));
+  if (!err_msg.empty())
+    return RespondNow(Error(err_msg));
 
-  profile->GetPrefs()->SetBoolean(arc::prefs::kVoiceInteractionEnabled,
-                                  params->enabled);
   // |NOT_READY| means service not running
   // |STOPPED| means service running but UI not shown
   auto new_state = params->enabled
@@ -1368,9 +1369,11 @@
   EXTENSION_FUNCTION_VALIDATE(params);
 
   Profile* profile = Profile::FromBrowserContext(browser_context());
-  if (!profile || assistant::IsAssistantAllowedForProfile(profile) !=
-                      ash::mojom::AssistantAllowedState::ALLOWED) {
-    return RespondNow(Error("Assistant is not available for the current user"));
+  ash::mojom::AssistantAllowedState allowed_state =
+      assistant::IsAssistantAllowedForProfile(profile);
+  if (allowed_state != ash::mojom::AssistantAllowedState::ALLOWED) {
+    return RespondNow(Error(base::StringPrintf(
+        "Assistant not allowed - state: %d", allowed_state)));
   }
 
   // Bind to Assistant service interface.
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 659d06f..111adbe4 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -578,6 +578,7 @@
         TestCase("dirCreateWithoutChangingCurrent").EnableMyFilesVolume(),
         TestCase("dirCreateWithoutChangingCurrent"),
         TestCase("dirContextMenuRecent"),
+        ZipCase("dirContextMenuZip"),
         TestCase("dirContextMenuMyFiles").EnableMyFilesVolume(),
         TestCase("dirContextMenuCrostini"),
         TestCase("dirContextMenuPlayFiles"),
@@ -585,6 +586,7 @@
         TestCase("dirContextMenuFsp"),
         TestCase("dirContextMenuDocumentsProvider").EnableDocumentsProvider(),
         TestCase("dirContextMenuUsbDcim"),
+        TestCase("dirContextMenuMtp"),
         TestCase("dirContextMenuShortcut")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_common.cc b/chrome/browser/chromeos/login/ui/login_display_host_common.cc
index 4a16485b..fde3109 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_common.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_common.cc
@@ -282,17 +282,7 @@
       GetOobeUI()->GetGaiaScreenView()->ShowGaiaAsync(prefilled_account);
     LoadWallpaper(*prefilled_account);
   } else {
-    // Two criteria here:
-    // 1) If we have started a wizard other than Gaia signin (signified by the
-    // current_screen() changing), we need to reload the Gaia screen, otherwise
-    // dialog_->Show() will show the wrong screen.
-    // 2) While login is being loaded in, the current_screen is UNKNOWN. During
-    // this time, the GaiaScreenView is initialized, after which
-    // ShowGaiaAsync() is called to load up the Gaia screen. If we try to
-    // ShowGaiaAsync() before this initialization is complete, the Gaia screen
-    // UI can crash and get stuck.
-    if (GetOobeUI()->current_screen() != OobeScreen::SCREEN_GAIA_SIGNIN &&
-        GetOobeUI()->current_screen() != OobeScreen::SCREEN_UNKNOWN) {
+    if (GetOobeUI()->current_screen() != OobeScreen::SCREEN_GAIA_SIGNIN) {
       GetOobeUI()->GetGaiaScreenView()->ShowGaiaAsync(base::nullopt);
     }
     LoadSigninWallpaper();
diff --git a/chrome/browser/chromeos/policy/user_affiliation_browsertest.cc b/chrome/browser/chromeos/policy/user_affiliation_browsertest.cc
index 8b8f4a8fd..be3a59e6 100644
--- a/chrome/browser/chromeos/policy/user_affiliation_browsertest.cc
+++ b/chrome/browser/chromeos/policy/user_affiliation_browsertest.cc
@@ -207,6 +207,16 @@
     policy::DeviceManagementService::SetRetryDelayForTesting(0);
   }
 
+  void CreatedBrowserMainParts(
+      content::BrowserMainParts* browser_main_parts) override {
+    InProcessBrowserTest::CreatedBrowserMainParts(browser_main_parts);
+
+    login_ui_visible_waiter_ =
+        std::make_unique<content::WindowedNotificationObserver>(
+            chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
+            content::NotificationService::AllSources());
+  }
+
   // InProcessBrowserTest:
   void SetUpOnMainThread() override {
     InProcessBrowserTest::SetUpOnMainThread();
@@ -215,10 +225,7 @@
       // This is a workaround for chrome crashing when running with DCHECKS when
       // it exits while the login manager is being loaded.
       // TODO(pmarko): Remove this when https://crbug.com/869272 is fixed.
-      content::WindowedNotificationObserver(
-          chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
-          content::NotificationService::AllSources())
-          .Wait();
+      login_ui_visible_waiter_->Wait();
     }
   }
 
@@ -283,6 +290,9 @@
 
   std::unique_ptr<crypto::ScopedTestSystemNSSKeySlot> test_system_slot_;
 
+  std::unique_ptr<content::WindowedNotificationObserver>
+      login_ui_visible_waiter_;
+
   DISALLOW_COPY_AND_ASSIGN(UserAffiliationBrowserTest);
 };
 
diff --git a/chrome/browser/chromeos/profiles/profile_util.cc b/chrome/browser/chromeos/profiles/profile_util.cc
index 31e97929..e4b11061 100644
--- a/chrome/browser/chromeos/profiles/profile_util.cc
+++ b/chrome/browser/chromeos/profiles/profile_util.cc
@@ -8,10 +8,12 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/login/login_state/login_state.h"
+#include "components/user_manager/user.h"
 
 namespace chromeos {
 
 bool IsProfileAssociatedWithGaiaAccount(Profile* profile) {
+  // TODO(crbug.com/942937): This code can likely be simplified.
   if (!chromeos::LoginState::IsInitialized())
     return false;
   if (!chromeos::LoginState::Get()->IsUserGaiaAuthenticated())
@@ -27,6 +29,12 @@
     return false;
   if (profile->GetPath() == ProfileHelper::GetLockScreenAppProfilePath())
     return false;
+  const user_manager::User* user =
+      chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+  if (user && user->GetType() == user_manager::USER_TYPE_ACTIVE_DIRECTORY) {
+    return false;
+  }
+
   return true;
 }
 
diff --git a/chrome/browser/chromeos/set_time_dialog.cc b/chrome/browser/chromeos/set_time_dialog.cc
index 56cbf97..d96d75a 100644
--- a/chrome/browser/chromeos/set_time_dialog.cc
+++ b/chrome/browser/chromeos/set_time_dialog.cc
@@ -7,6 +7,8 @@
 #include "base/metrics/user_metrics.h"
 #include "base/strings/string16.h"
 #include "chrome/common/url_constants.h"
+#include "chromeos/constants/chromeos_features.h"
+#include "chromeos/login/login_state/login_state.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace chromeos {
@@ -16,6 +18,11 @@
 const int kDefaultWidth = 490;
 const int kDefaultHeight = 235;
 
+// Material design dialog width and height in DIPs.
+const int kDefaultWidthMd = 530;
+const int kDefaultHeightWithTimezone = 255;
+const int kDefaultHeightWithoutTimezone = 215;
+
 }  // namespace
 
 // static
@@ -25,6 +32,13 @@
   dialog->ShowSystemDialog(parent);
 }
 
+// static
+bool SetTimeDialog::ShouldShowTimezone() {
+  // After login the user should set the timezone via Settings, which applies
+  // additional restrictions.
+  return !LoginState::Get()->IsUserLoggedIn();
+}
+
 SetTimeDialog::SetTimeDialog()
     : SystemWebDialogDelegate(GURL(chrome::kChromeUISetTimeURL),
                               base::string16() /* title */) {}
@@ -32,7 +46,13 @@
 SetTimeDialog::~SetTimeDialog() = default;
 
 void SetTimeDialog::GetDialogSize(gfx::Size* size) const {
-  size->SetSize(kDefaultWidth, kDefaultHeight);
+  if (features::IsSetTimeDialogMd()) {
+    size->SetSize(kDefaultWidthMd, ShouldShowTimezone()
+                                       ? kDefaultHeightWithTimezone
+                                       : kDefaultHeightWithoutTimezone);
+  } else {
+    size->SetSize(kDefaultWidth, kDefaultHeight);
+  }
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/set_time_dialog.h b/chrome/browser/chromeos/set_time_dialog.h
index a2368b1..cf174d4 100644
--- a/chrome/browser/chromeos/set_time_dialog.h
+++ b/chrome/browser/chromeos/set_time_dialog.h
@@ -20,6 +20,9 @@
   // as a child of |parent|, e.g. the Settings window.
   static void ShowDialog(gfx::NativeWindow parent = nullptr);
 
+  // Returns true if the dialog should show the timezone <select>.
+  static bool ShouldShowTimezone();
+
  private:
   SetTimeDialog();
   ~SetTimeDialog() override;
diff --git a/chrome/browser/chromeos/system/timezone_util.cc b/chrome/browser/chromeos/system/timezone_util.cc
index fe2451a..5c0d4e8 100644
--- a/chrome/browser/chromeos/system/timezone_util.cc
+++ b/chrome/browser/chromeos/system/timezone_util.cc
@@ -381,7 +381,9 @@
   Profile* primary_profile = ProfileManager::GetPrimaryUserProfile();
   if (primary_profile && profile->IsSameProfile(primary_profile)) {
     profile->GetPrefs()->SetString(prefs::kUserTimezone, timezone_id);
+    return;
   }
+
   // Time zone UI should be blocked for non-primary users.
   NOTREACHED();
 }
diff --git a/chrome/browser/chromeos/usb/OWNERS b/chrome/browser/chromeos/usb/OWNERS
new file mode 100644
index 0000000..9d9bf0c4
--- /dev/null
+++ b/chrome/browser/chromeos/usb/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/chromeos/crostini/OWNERS
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector.cc b/chrome/browser/chromeos/usb/cros_usb_detector.cc
index 19c2025..06e928b 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector.cc
+++ b/chrome/browser/chromeos/usb/cros_usb_detector.cc
@@ -8,13 +8,20 @@
 #include <utility>
 
 #include "base/metrics/histogram_macros.h"
+#include "chrome/browser/chromeos/crostini/crostini_manager.h"
+#include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
+#include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/notifications/system_notification_helper.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/constants/chromeos_features.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/vector_icons/vector_icons.h"
 #include "content/public/common/service_manager_connection.h"
 #include "device/usb/public/cpp/usb_utils.h"
+#include "device/usb/public/mojom/device_enumeration_options.mojom.h"
 #include "services/device/public/mojom/constants.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -23,30 +30,63 @@
 
 namespace {
 
+// Not owned locally.
+static CrosUsbDetector* g_cros_usb_detector = nullptr;
+
 const char kNotifierUsb[] = "crosusb.connected";
 
 void RecordNotificationClosure(CrosUsbNotificationClosed disposition) {
   UMA_HISTOGRAM_ENUMERATION("CrosUsb.NotificationClosed", disposition);
 }
 
-// Delegate for CrOSUsb notification
-class UsbNotificationDelegate : public message_center::NotificationDelegate {
+base::string16 ProductLabelFromDevice(
+    const device::mojom::UsbDeviceInfoPtr& device_info) {
+  base::string16 product_label =
+      l10n_util::GetStringUTF16(IDS_CROSUSB_UNKNOWN_DEVICE);
+  if (device_info->product_name.has_value() &&
+      !device_info->product_name->empty()) {
+    product_label = device_info->product_name.value();
+  } else if (device_info->manufacturer_name.has_value() &&
+             !device_info->manufacturer_name->empty()) {
+    product_label =
+        l10n_util::GetStringFUTF16(IDS_CROSUSB_UNKNOWN_DEVICE_FROM_MANUFACTURER,
+                                   device_info->manufacturer_name.value());
+  }
+  return product_label;
+}
+
+Profile* profile() {
+  return ProfileManager::GetActiveUserProfile();
+}
+
+crostini::CrostiniManager* manager() {
+  return crostini::CrostiniManager::GetForProfile(profile());
+}
+
+// Delegate for CrosUsb notification
+class CrosUsbNotificationDelegate
+    : public message_center::NotificationDelegate {
  public:
-  explicit UsbNotificationDelegate(const std::string& notification_id,
-                                   const std::string& device_id)
+  explicit CrosUsbNotificationDelegate(
+      const std::string& notification_id,
+      device::mojom::UsbDeviceInfoPtr device_info)
       : notification_id_(notification_id),
-        device_id_(device_id),
+        device_info_(std::move(device_info)),
         disposition_(CrosUsbNotificationClosed::kUnknown) {}
 
   void Click(const base::Optional<int>& button_index,
              const base::Optional<base::string16>& reply) override {
-    disposition_ = CrosUsbNotificationClosed::kConnectToLinux;
-    // TODO(jopra): add device_id_ to shared USBs list (instead of opening
-    // settings page)
-    chrome::ShowSettingsSubPageForProfile(
-        ProfileManager::GetActiveUserProfile(),
-        chrome::kCrostiniSharedUsbDevicesSubPage);
-    Close(false);
+    disposition_ = CrosUsbNotificationClosed::kUnknown;
+    if (button_index) {
+      switch (button_index.value()) {
+        case 0:
+          HandleConnectToVm();
+          break;
+        case 1:
+          HandleShowSettings();
+          break;
+      }
+    }
   }
 
   void Close(bool by_user) override {
@@ -56,13 +96,29 @@
   }
 
  private:
-  ~UsbNotificationDelegate() override = default;
+  ~CrosUsbNotificationDelegate() override = default;
+  void HandleConnectToVm() {
+    disposition_ = CrosUsbNotificationClosed::kConnectToLinux;
+    chromeos::CrosUsbDetector* detector = chromeos::CrosUsbDetector::Get();
+    if (detector) {
+      detector->AttachUsbDeviceToVm(crostini::kCrostiniDefaultVmName,
+                                    device_info_->guid, base::DoNothing());
+    }
+    Close(false);
+  }
+
+  void HandleShowSettings() {
+    chrome::ShowSettingsSubPageForProfile(
+        ProfileManager::GetActiveUserProfile(),
+        chrome::kCrostiniSharedUsbDevicesSubPage);
+    Close(false);
+  }
 
   std::string notification_id_;
-  std::string device_id_;
+  device::mojom::UsbDeviceInfoPtr device_info_;
   CrosUsbNotificationClosed disposition_;
 
-  DISALLOW_COPY_AND_ASSIGN(UsbNotificationDelegate);
+  DISALLOW_COPY_AND_ASSIGN(CrosUsbNotificationDelegate);
 };
 
 // List of class codes to handle / not handle.
@@ -99,47 +155,52 @@
 }
 
 void ShowNotificationForDevice(device::mojom::UsbDeviceInfoPtr device_info) {
-  base::string16 product_name =
-      l10n_util::GetStringUTF16(IDS_CROSUSB_UNKNOWN_DEVICE);
-  if (device_info->product_name.has_value() &&
-      !device_info->product_name->empty()) {
-    product_name = device_info->product_name.value();
-  } else if (device_info->manufacturer_name.has_value() &&
-             !device_info->manufacturer_name->empty()) {
-    product_name =
-        l10n_util::GetStringFUTF16(IDS_CROSUSB_UNKNOWN_DEVICE_FROM_MANUFACTURER,
-                                   device_info->manufacturer_name.value());
-  }
-
   message_center::RichNotificationData rich_notification_data;
   rich_notification_data.buttons.emplace_back(
       message_center::ButtonInfo(l10n_util::GetStringUTF16(
           IDS_CROSUSB_NOTIFICATION_BUTTON_CONNECT_TO_LINUX)));
+  rich_notification_data.buttons.emplace_back(
+      message_center::ButtonInfo(l10n_util::GetStringUTF16(
+          IDS_CROSUSB_NOTIFICATION_BUTTON_VIEW_SETTINGS)));
 
   std::string notification_id =
       CrosUsbDetector::MakeNotificationId(device_info->guid);
   message_center::Notification notification(
       message_center::NOTIFICATION_TYPE_MULTIPLE, notification_id,
-      l10n_util::GetStringFUTF16(IDS_CROSUSB_DEVICE_DETECTED_NOTIFICATION_TITLE,
-                                 product_name),
-      l10n_util::GetStringUTF16(IDS_CROSUSB_DEVICE_DETECTED_NOTIFICATION),
-      gfx::Image(), base::string16(), GURL(),
+      l10n_util::GetStringUTF16(IDS_CROSUSB_DEVICE_DETECTED_NOTIFICATION_TITLE),
+      l10n_util::GetStringFUTF16(IDS_CROSUSB_DEVICE_DETECTED_NOTIFICATION,
+                                 ProductLabelFromDevice(device_info)),
+      gfx::Image(gfx::CreateVectorIcon(vector_icons::kUsbIcon, 64,
+                                       gfx::kGoogleBlue800)),
+      base::string16(), GURL(),
       message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
                                  kNotifierUsb),
       rich_notification_data,
-      base::MakeRefCounted<UsbNotificationDelegate>(notification_id,
-                                                    device_info->guid));
+      base::MakeRefCounted<CrosUsbNotificationDelegate>(
+          notification_id, std::move(device_info)));
   notification.SetSystemPriority();
   SystemNotificationHelper::GetInstance()->Display(notification);
 }
 
 }  // namespace
 
+SharedUsbDeviceInfo::SharedUsbDeviceInfo() = default;
+SharedUsbDeviceInfo::SharedUsbDeviceInfo(const SharedUsbDeviceInfo&) = default;
+SharedUsbDeviceInfo::~SharedUsbDeviceInfo() = default;
+
 std::string CrosUsbDetector::MakeNotificationId(const std::string& guid) {
   return "cros:" + guid;
 }
 
-CrosUsbDetector::CrosUsbDetector() : client_binding_(this) {
+// static
+CrosUsbDetector* CrosUsbDetector::Get() {
+  return g_cros_usb_detector;
+}
+
+CrosUsbDetector::CrosUsbDetector()
+    : client_binding_(this), weak_ptr_factory_(this) {
+  DCHECK(!g_cros_usb_detector);
+  g_cros_usb_detector = this;
   guest_os_classes_blocked_.emplace_back(
       UsbFilterByClassCode(USB_CLASS_PHYSICAL));
   guest_os_classes_blocked_.emplace_back(UsbFilterByClassCode(USB_CLASS_HID));
@@ -158,36 +219,64 @@
       UsbFilterByClassCode(USB_CLASS_PERSONAL_HEALTHCARE));
 }
 
-CrosUsbDetector::~CrosUsbDetector() {}
+CrosUsbDetector::~CrosUsbDetector() {
+  DCHECK_EQ(this, g_cros_usb_detector);
+  g_cros_usb_detector = nullptr;
+}
 
 void CrosUsbDetector::SetDeviceManagerForTesting(
     device::mojom::UsbDeviceManagerPtr device_manager) {
+  DCHECK(!device_manager_) << "device_manager_ was already initialized";
   device_manager_ = std::move(device_manager);
 }
 
+void CrosUsbDetector::AddSharedUsbDeviceObserver(
+    SharedUsbDeviceObserver* observer) {
+  shared_usb_device_observers_.AddObserver(observer);
+}
+
+void CrosUsbDetector::RemoveSharedUsbDeviceObserver(
+    SharedUsbDeviceObserver* observer) {
+  shared_usb_device_observers_.RemoveObserver(observer);
+}
+
+void CrosUsbDetector::SignalSharedUsbDeviceObservers() {
+  for (auto& observer : shared_usb_device_observers_) {
+    observer.OnSharedUsbDevicesChanged(shared_usb_devices_);
+  }
+}
+
+std::vector<SharedUsbDeviceInfo> CrosUsbDetector::GetSharedUsbDevices() {
+  return shared_usb_devices_;
+}
+
 void CrosUsbDetector::ConnectToDeviceManager() {
   // Tests may set a fake manager.
   if (!device_manager_) {
     // Request UsbDeviceManagerPtr from DeviceService.
-    content::ServiceManagerConnection::GetForProcess()
-        ->GetConnector()
-        ->BindInterface(device::mojom::kServiceName,
-                        mojo::MakeRequest(&device_manager_));
+    auto* connection = content::ServiceManagerConnection::GetForProcess();
+    DCHECK(connection);
+    connection->GetConnector()->BindInterface(
+        device::mojom::kServiceName, mojo::MakeRequest(&device_manager_));
   }
   DCHECK(device_manager_);
   device_manager_.set_connection_error_handler(
       base::BindOnce(&CrosUsbDetector::OnDeviceManagerConnectionError,
-                     base::Unretained(this)));
+                     weak_ptr_factory_.GetWeakPtr()));
 
   // Listen for added/removed device events.
   DCHECK(!client_binding_);
   device::mojom::UsbDeviceManagerClientAssociatedPtrInfo client;
   client_binding_.Bind(mojo::MakeRequest(&client));
-  device_manager_->SetClient(std::move(client));
+
+  device_manager_->EnumerateDevicesAndSetClient(
+      std::move(client), base::BindOnce(&CrosUsbDetector::OnListAttachedDevices,
+                                        weak_ptr_factory_.GetWeakPtr()));
 }
 
 void CrosUsbDetector::OnDeviceChecked(
     device::mojom::UsbDeviceInfoPtr device_info,
+    bool hide_notification,
     bool allowed) {
   if (!allowed) {
     LOG(WARNING) << "Device not allowed by Permission Broker. product:"
@@ -196,40 +285,201 @@
     return;
   }
 
-  // TODO(jopra): Enable device connection management for allowed, non-blocked
-  // devices.
+  SharedUsbDeviceInfo new_device;
+  new_device.vm_name = crostini::kCrostiniDefaultVmName;
+  new_device.guid = device_info->guid;
+  new_device.label = ProductLabelFromDevice(device_info);
+  new_device.shared = false;
+  new_device.guest_port = base::nullopt;
+
+  shared_usb_devices_.push_back(new_device);
+  available_device_info_.emplace(device_info->guid, device_info.Clone());
+  SignalSharedUsbDeviceObservers();
 
   // Some devices should not trigger the notification.
-  if (device::UsbDeviceFilterMatchesAny(guest_os_classes_without_notif_,
-                                        *device_info)) {
+  if (hide_notification || device::UsbDeviceFilterMatchesAny(
+                               guest_os_classes_without_notif_, *device_info)) {
     return;
   }
   ShowNotificationForDevice(std::move(device_info));
 }
 
-void CrosUsbDetector::OnDeviceAdded(
-    device::mojom::UsbDeviceInfoPtr device_info) {
+void CrosUsbDetector::OnDeviceAdded(device::mojom::UsbDeviceInfoPtr device) {
+  CrosUsbDetector::OnDeviceAdded(std::move(device), false);
+}
+
+void CrosUsbDetector::OnDeviceAdded(device::mojom::UsbDeviceInfoPtr device_info,
+                                    bool hide_notification) {
   if (device::UsbDeviceFilterMatchesAny(guest_os_classes_blocked_,
                                         *device_info)) {
     return;  // Guest OS does not handle this kind of device.
   }
   device_manager_->CheckAccess(
       device_info->guid,
-      base::BindOnce(&CrosUsbDetector::OnDeviceChecked, base::Unretained(this),
-                     std::move(device_info)));
+      base::BindOnce(&CrosUsbDetector::OnDeviceChecked,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(device_info),
+                     hide_notification));
 }
 
 void CrosUsbDetector::OnDeviceRemoved(
     device::mojom::UsbDeviceInfoPtr device_info) {
   SystemNotificationHelper::GetInstance()->Close(
       CrosUsbDetector::MakeNotificationId(device_info->guid));
+
+  std::string guid = device_info->guid;
+  for (const auto& device : shared_usb_devices_) {
+    if (device.guid == guid) {
+      DetachUsbDeviceFromVm(device.vm_name, guid, base::DoNothing());
+    }
+  }
+  const auto& start =
+      std::remove_if(shared_usb_devices_.begin(), shared_usb_devices_.end(),
+                     [guid](const SharedUsbDeviceInfo& device) {
+                       return device.guid == guid;
+                     });
+  if (start != shared_usb_devices_.end()) {
+    shared_usb_devices_.erase(start, shared_usb_devices_.end());
+  }
+  available_device_info_.erase(guid);
+  SignalSharedUsbDeviceObservers();
 }
 
 void CrosUsbDetector::OnDeviceManagerConnectionError() {
   device_manager_.reset();
   client_binding_.Close();
-
   ConnectToDeviceManager();
 }
 
+void CrosUsbDetector::ConnectSharedDevicesOnVmStartup() {
+  // Reattach shared devices when the VM becomes available.
+  for (auto& device : shared_usb_devices_) {
+    if (device.shared) {
+      AttachUsbDeviceToVm(crostini::kCrostiniDefaultVmName, device.guid,
+                          base::DoNothing());
+    }
+  }
+}
+
+void CrosUsbDetector::AttachUsbDeviceToVm(
+    const std::string& vm_name,
+    const std::string& guid,
+    crostini::CrostiniManager::CrostiniResultCallback callback) {
+  auto it = available_device_info_.find(guid);
+  if (it == available_device_info_.end()) {
+    return;
+  }
+
+  for (auto& device : shared_usb_devices_) {
+    if (device.guid == guid) {
+      // Mark the USB Device shared so that we know to reattach it on VM
+      // restart.
+      device.shared = true;
+    }
+  }
+
+  const auto& device_info = it->second;
+  // Close any associated notifications (the user isn't using them).
+  SystemNotificationHelper::GetInstance()->Close(
+      CrosUsbDetector::MakeNotificationId(guid));
+  // Open a file descriptor to pass to CrostiniManager & Concierge.
+  device_manager_->OpenFileDescriptor(
+      guid, base::BindOnce(&CrosUsbDetector::OnAttachUsbDeviceOpened,
+                           weak_ptr_factory_.GetWeakPtr(), vm_name,
+                           device_info.Clone(), std::move(callback)));
+}
+
+void CrosUsbDetector::DetachUsbDeviceFromVm(
+    const std::string& vm_name,
+    const std::string& guid,
+    crostini::CrostiniManager::CrostiniResultCallback callback) {
+  const auto& it = available_device_info_.find(guid);
+  if (it == available_device_info_.end()) {
+    // If there wasn't an existing attachment, then removal is a no-op and
+    // always succeeds
+    std::move(callback).Run(crostini::CrostiniResult::SUCCESS);
+    return;
+  }
+  const auto& device_info = it->second;
+
+  base::Optional<uint8_t> guest_port;
+  for (const auto& device : shared_usb_devices_) {
+    if (device.guid == guid) {
+      guest_port = device.guest_port;
+      break;
+    }
+  }
+
+  if (!guest_port) {
+    std::move(callback).Run(crostini::CrostiniResult::SUCCESS);
+    return;
+  }
+  manager()->DetachUsbDevice(
+      vm_name, device_info.Clone(), *guest_port,
+      base::BindOnce(&CrosUsbDetector::OnUsbDeviceDetachFinished,
+                     weak_ptr_factory_.GetWeakPtr(), vm_name, guid,
+                     std::move(callback), *guest_port));
+}
+
+void CrosUsbDetector::OnListAttachedDevices(
+    std::vector<device::mojom::UsbDeviceInfoPtr> devices) {
+  for (device::mojom::UsbDeviceInfoPtr& device_info : devices)
+    CrosUsbDetector::OnDeviceAdded(std::move(device_info),
+                                   /*hide_notification*/ true);
+}
+
+void CrosUsbDetector::OnAttachUsbDeviceOpened(
+    const std::string& vm_name,
+    device::mojom::UsbDeviceInfoPtr device_info,
+    crostini::CrostiniManager::CrostiniResultCallback callback,
+    base::File file) {
+  if (!file.IsValid()) {
+    LOG(ERROR) << "Permission broker refused access to USB device";
+    std::move(callback).Run(crostini::CrostiniResult::PERMISSION_BROKER_ERROR);
+    return;
+  }
+
+  base::ScopedFD fd(file.TakePlatformFile());
+
+  const std::string guid = device_info->guid;
+  manager()->AttachUsbDevice(
+      vm_name, std::move(device_info), std::move(fd),
+      base::BindOnce(&CrosUsbDetector::OnUsbDeviceAttachFinished,
+                     weak_ptr_factory_.GetWeakPtr(), vm_name, guid,
+                     std::move(callback)));
+}
+
+void CrosUsbDetector::OnUsbDeviceAttachFinished(
+    const std::string& vm_name,
+    const std::string& guid,
+    crostini::CrostiniManager::CrostiniResultCallback callback,
+    uint8_t guest_port,
+    crostini::CrostiniResult result) {
+  if (result == crostini::CrostiniResult::SUCCESS) {
+    for (auto& device : shared_usb_devices_) {
+      if (device.guid == guid) {
+        device.guest_port = guest_port;
+      }
+    }
+  }
+  SignalSharedUsbDeviceObservers();
+  std::move(callback).Run(result);
+}
+
+void CrosUsbDetector::OnUsbDeviceDetachFinished(
+    const std::string& vm_name,
+    const std::string& guid,
+    crostini::CrostiniManager::CrostiniResultCallback callback,
+    uint8_t guest_port,
+    crostini::CrostiniResult result) {
+  for (auto& device : shared_usb_devices_) {
+    if (device.guid == guid) {
+      device.guest_port = base::nullopt;
+      device.shared = false;
+      break;
+    }
+  }
+  SignalSharedUsbDeviceObservers();
+  std::move(callback).Run(result);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector.h b/chrome/browser/chromeos/usb/cros_usb_detector.h
index 6b34b14..f4651df 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector.h
+++ b/chrome/browser/chromeos/usb/cros_usb_detector.h
@@ -5,9 +5,15 @@
 #ifndef CHROME_BROWSER_CHROMEOS_USB_CROS_USB_DETECTOR_H_
 #define CHROME_BROWSER_CHROMEOS_USB_CROS_USB_DETECTOR_H_
 
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
 #include <vector>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/crostini/crostini_manager.h"
 #include "device/usb/public/mojom/device_enumeration_options.mojom.h"
 #include "device/usb/public/mojom/device_manager.mojom.h"
 #include "device/usb/public/mojom/device_manager_client.mojom.h"
@@ -15,6 +21,8 @@
 
 namespace chromeos {
 
+const uint8_t kInvalidUsbPortNumber = 0xff;
+
 // Reasons the notification may be closed. These are used in histograms so do
 // not remove/reorder entries. Only add at the end just before kMaxValue. Also
 // remember to update the enum listing in
@@ -31,6 +39,26 @@
   kMaxValue = kConnectToLinux
 };
 
+struct SharedUsbDeviceInfo {
+  SharedUsbDeviceInfo();
+  SharedUsbDeviceInfo(const SharedUsbDeviceInfo&);
+  ~SharedUsbDeviceInfo();
+
+  std::string vm_name;
+  std::string guid;
+  base::string16 label;
+  bool shared = false;
+  base::Optional<uint8_t> guest_port;
+  // TODO(nverne): Add current state and errors etc.
+};
+
+class SharedUsbDeviceObserver : public base::CheckedObserver {
+ public:
+  // Called when the available USB devices change.
+  virtual void OnSharedUsbDevicesChanged(
+      std::vector<SharedUsbDeviceInfo> usb_devices) = 0;
+};
+
 // Detects USB Devices for Chrome OS and manages UI for controlling their use
 // with CrOS, Web or GuestOSs.
 class CrosUsbDetector : public device::mojom::UsbDeviceManagerClient {
@@ -38,6 +66,9 @@
   // Used to namespace USB notifications to avoid clashes with WebUsbDetector.
   static std::string MakeNotificationId(const std::string& guid);
 
+  // Can return nullptr.
+  static CrosUsbDetector* Get();
+
   CrosUsbDetector();
   ~CrosUsbDetector() override;
 
@@ -49,16 +80,66 @@
   // device manager during testing.
   void ConnectToDeviceManager();
 
- private:
-  // Called after USB device access has been checked.
-  void OnDeviceChecked(device::mojom::UsbDeviceInfoPtr device, bool allowed);
+  // Called by CrostiniManager when a VM starts, to attach USB devices marked as
+  // shared to the VM.
+  void ConnectSharedDevicesOnVmStartup();
 
-  // device::mojom::UsbDeviceManagerClient implementation.
+  // device::mojom::UsbDeviceManagerClient
   void OnDeviceAdded(device::mojom::UsbDeviceInfoPtr device) override;
   void OnDeviceRemoved(device::mojom::UsbDeviceInfoPtr device) override;
 
+  void AttachUsbDeviceToVm(
+      const std::string& vm_name,
+      const std::string& guid,
+      crostini::CrostiniManager::CrostiniResultCallback callback);
+  void DetachUsbDeviceFromVm(
+      const std::string& vm_name,
+      const std::string& guid,
+      crostini::CrostiniManager::CrostiniResultCallback callback);
+
+  void AddSharedUsbDeviceObserver(SharedUsbDeviceObserver* observer);
+  void RemoveSharedUsbDeviceObserver(SharedUsbDeviceObserver* observer);
+  void SignalSharedUsbDeviceObservers();
+
+  std::vector<SharedUsbDeviceInfo> GetSharedUsbDevices();
+
+ private:
+  // Called after USB device access has been checked.
+  void OnDeviceChecked(device::mojom::UsbDeviceInfoPtr device,
+                       bool hide_notification,
+                       bool allowed);
+
+  // Allows the notification to be hidden (OnDeviceAdded without the flag calls
+  // this).
+  void OnDeviceAdded(device::mojom::UsbDeviceInfoPtr device,
+                     bool hide_notification);
   void OnDeviceManagerConnectionError();
 
+  // Callback listing devices attached to the machine.
+  void OnListAttachedDevices(
+      std::vector<device::mojom::UsbDeviceInfoPtr> devices);
+
+  // Callback for AttachUsbDeviceToVm after opening a file handler.
+  void OnAttachUsbDeviceOpened(
+      const std::string& vm_name,
+      device::mojom::UsbDeviceInfoPtr device,
+      crostini::CrostiniManager::CrostiniResultCallback callback,
+      base::File file);
+
+  // Callbacks for when the USB device state has been updated.
+  void OnUsbDeviceAttachFinished(
+      const std::string& vm_name,
+      const std::string& guid,
+      crostini::CrostiniManager::CrostiniResultCallback callback,
+      uint8_t guest_port,
+      crostini::CrostiniResult result);
+  void OnUsbDeviceDetachFinished(
+      const std::string& vm_name,
+      const std::string& guid,
+      crostini::CrostiniManager::CrostiniResultCallback callback,
+      uint8_t guest_port,
+      crostini::CrostiniResult result);
+
   device::mojom::UsbDeviceManagerPtr device_manager_;
   mojo::AssociatedBinding<device::mojom::UsbDeviceManagerClient>
       client_binding_;
@@ -67,6 +148,17 @@
   std::vector<device::mojom::UsbDeviceFilterPtr>
       guest_os_classes_without_notif_;
 
+  // A mapping from GUID -> UsbDeviceInfo for each attached USB device
+  std::map<std::string, device::mojom::UsbDeviceInfoPtr> available_device_info_;
+
+  std::vector<SharedUsbDeviceInfo> shared_usb_devices_;
+
+  base::ObserverList<SharedUsbDeviceObserver> shared_usb_device_observers_;
+
+  // Note: This should remain the last member so it'll be destroyed and
+  // invalidate its weak pointers before any other members are destroyed.
+  base::WeakPtrFactory<CrosUsbDetector> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(CrosUsbDetector);
 };
 
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc b/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc
index 2b4cfd2..9db1527 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc
+++ b/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc
@@ -9,6 +9,8 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/chromeos/crostini/crostini_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/usb/cros_usb_detector.h"
 #include "chrome/browser/notifications/notification_display_service.h"
@@ -18,6 +20,10 @@
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "chromeos/constants/chromeos_features.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_cicerone_client.h"
+#include "chromeos/dbus/fake_concierge_client.h"
 #include "device/usb/public/cpp/fake_usb_device_info.h"
 #include "device/usb/public/cpp/fake_usb_device_manager.h"
 #include "device/usb/public/mojom/device.mojom.h"
@@ -34,8 +40,8 @@
 const char* kProductName_1 = "Google Product A";
 const char* kProductName_2 = "Google Product B";
 const char* kProductName_3 = "Google Product C";
+const char* kUnknownProductName = "USB device";
 const char* kManufacturerName = "Google";
-const char* kExpectedConnectionMessage = "Connect this device to Linux";
 
 const int kUsbConfigWithInterfaces = 1;
 
@@ -74,15 +80,37 @@
 
 class CrosUsbDetectorTest : public BrowserWithTestWindowTest {
  public:
-  CrosUsbDetectorTest() {}
-  ~CrosUsbDetectorTest() override = default;
+  CrosUsbDetectorTest() {
+    chromeos::DBusThreadManager::Initialize();
+    fake_cicerone_client_ = static_cast<chromeos::FakeCiceroneClient*>(
+        chromeos::DBusThreadManager::Get()->GetCiceroneClient());
+    fake_concierge_client_ = static_cast<chromeos::FakeConciergeClient*>(
+        chromeos::DBusThreadManager::Get()->GetConciergeClient());
+    cros_usb_detector_ = std::make_unique<chromeos::CrosUsbDetector>();
+  }
+
+  ~CrosUsbDetectorTest() override { chromeos::DBusThreadManager::Shutdown(); }
 
   TestingProfile* CreateProfile() override {
     return profile_manager()->CreateTestingProfile(kProfileName);
   }
 
+  void AttachUsbDeviceToVmSuccessCallback(base::OnceClosure closure,
+                                          crostini::CrostiniResult result) {
+    EXPECT_TRUE(fake_concierge_client_->attach_usb_device_called());
+    std::move(closure).Run();
+  }
+
+  void DetachUsbDeviceToVmSuccessCallback(base::OnceClosure closure,
+                                          crostini::CrostiniResult result) {
+    EXPECT_TRUE(fake_concierge_client_->detach_usb_device_called());
+    std::move(closure).Run();
+  }
+
   void SetUp() override {
     BrowserWithTestWindowTest::SetUp();
+    scoped_feature_list_.InitAndEnableFeature(
+        chromeos::features::kCrostiniUsbSupport);
     profile_manager()->SetLoggedIn(true);
     chromeos::ProfileHelper::Get()->SetActiveUserIdForTesting(kProfileName);
     TestingBrowserProcess::GetGlobal()->SetSystemNotificationHelper(
@@ -90,28 +118,42 @@
     display_service_ = std::make_unique<NotificationDisplayServiceTester>(
         nullptr /* profile */);
 
-    cros_usb_detector_.reset(new chromeos::CrosUsbDetector());
     // Set a fake USB device manager before ConnectToDeviceManager().
     device::mojom::UsbDeviceManagerPtr device_manager_ptr;
     device_manager_.AddBinding(mojo::MakeRequest(&device_manager_ptr));
-    cros_usb_detector_->SetDeviceManagerForTesting(
+    chromeos::CrosUsbDetector::Get()->SetDeviceManagerForTesting(
         std::move(device_manager_ptr));
   }
 
   void TearDown() override {
     BrowserWithTestWindowTest::TearDown();
-    cros_usb_detector_.reset();
   }
 
   void ConnectToDeviceManager() {
-    cros_usb_detector_->ConnectToDeviceManager();
+    chromeos::CrosUsbDetector::Get()->ConnectToDeviceManager();
   }
 
  protected:
+  base::string16 connection_message(const char* product_name) {
+    return base::ASCIIToUTF16(
+        base::StringPrintf("Connect %s to Linux", product_name));
+  }
+
+  base::string16 expected_title() {
+    return base::ASCIIToUTF16("USB device detected");
+  }
+
   device::FakeUsbDeviceManager device_manager_;
-  std::unique_ptr<chromeos::CrosUsbDetector> cros_usb_detector_;
   std::unique_ptr<NotificationDisplayServiceTester> display_service_;
 
+  // Owned by chromeos::DBusThreadManager
+  chromeos::FakeCiceroneClient* fake_cicerone_client_;
+  chromeos::FakeConciergeClient* fake_concierge_client_;
+
+  std::unique_ptr<chromeos::CrosUsbDetector> cros_usb_detector_;
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(CrosUsbDetectorTest);
 };
@@ -131,12 +173,9 @@
   base::Optional<message_center::Notification> notification =
       display_service_->GetNotification(notification_id);
   ASSERT_TRUE(notification);
-  base::string16 expected_title =
-      base::ASCIIToUTF16("Google Product A detected");
-  EXPECT_EQ(expected_title, notification->title());
-  base::string16 expected_message =
-      base::ASCIIToUTF16(kExpectedConnectionMessage);
-  EXPECT_EQ(expected_message, notification->message());
+
+  EXPECT_EQ(expected_title(), notification->title());
+  EXPECT_EQ(connection_message(kProductName_1), notification->message());
   EXPECT_TRUE(notification->delegate());
 
   device_manager_.RemoveDevice(device);
@@ -195,12 +234,10 @@
   base::Optional<message_center::Notification> notification =
       display_service_->GetNotification(notification_id);
   ASSERT_TRUE(notification);
-  base::string16 expected_title =
-      base::ASCIIToUTF16("USB device from Google detected");
-  EXPECT_EQ(expected_title, notification->title());
-  base::string16 expected_message =
-      base::ASCIIToUTF16(kExpectedConnectionMessage);
-  EXPECT_EQ(expected_message, notification->message());
+
+  EXPECT_EQ(expected_title(), notification->title());
+  EXPECT_EQ(connection_message("USB device from Google"),
+            notification->message());
   EXPECT_TRUE(notification->delegate());
 
   device_manager_.RemoveDevice(device);
@@ -227,11 +264,8 @@
   base::Optional<message_center::Notification> notification =
       display_service_->GetNotification(notification_id);
   ASSERT_TRUE(notification);
-  base::string16 expected_title = base::ASCIIToUTF16("USB device detected");
-  EXPECT_EQ(expected_title, notification->title());
-  base::string16 expected_message =
-      base::ASCIIToUTF16(kExpectedConnectionMessage);
-  EXPECT_EQ(expected_message, notification->message());
+  EXPECT_EQ(expected_title(), notification->title());
+  EXPECT_EQ(connection_message(kUnknownProductName), notification->message());
   EXPECT_TRUE(notification->delegate());
 
   device_manager_.RemoveDevice(device);
@@ -398,12 +432,9 @@
   base::Optional<message_center::Notification> notification =
       display_service_->GetNotification(notification_id_2);
   ASSERT_TRUE(notification);
-  base::string16 expected_title =
-      base::ASCIIToUTF16("Google Product B detected");
-  EXPECT_EQ(expected_title, notification->title());
-  base::string16 expected_message =
-      base::ASCIIToUTF16(kExpectedConnectionMessage);
-  EXPECT_EQ(expected_message, notification->message());
+
+  EXPECT_EQ(expected_title(), notification->title());
+  EXPECT_EQ(connection_message(kProductName_2), notification->message());
   EXPECT_TRUE(notification->delegate());
 
   device_manager_.RemoveDevice(device_2);
@@ -435,12 +466,9 @@
   base::Optional<message_center::Notification> notification_1 =
       display_service_->GetNotification(notification_id_1);
   ASSERT_TRUE(notification_1);
-  base::string16 expected_title_1 =
-      base::ASCIIToUTF16("Google Product A detected");
-  EXPECT_EQ(expected_title_1, notification_1->title());
-  base::string16 expected_message_1 =
-      base::ASCIIToUTF16(kExpectedConnectionMessage);
-  EXPECT_EQ(expected_message_1, notification_1->message());
+
+  EXPECT_EQ(expected_title(), notification_1->title());
+  EXPECT_EQ(connection_message(kProductName_1), notification_1->message());
   EXPECT_TRUE(notification_1->delegate());
 
   device_manager_.RemoveDevice(device_1);
@@ -452,12 +480,9 @@
   base::Optional<message_center::Notification> notification_2 =
       display_service_->GetNotification(notification_id_2);
   ASSERT_TRUE(notification_2);
-  base::string16 expected_title_2 =
-      base::ASCIIToUTF16("Google Product B detected");
-  EXPECT_EQ(expected_title_2, notification_2->title());
-  base::string16 expected_message_2 =
-      base::ASCIIToUTF16(kExpectedConnectionMessage);
-  EXPECT_EQ(expected_message_2, notification_2->message());
+
+  EXPECT_EQ(expected_title(), notification_2->title());
+  EXPECT_EQ(connection_message(kProductName_2), notification_2->message());
   EXPECT_TRUE(notification_2->delegate());
 
   device_manager_.RemoveDevice(device_2);
@@ -469,12 +494,9 @@
   base::Optional<message_center::Notification> notification_3 =
       display_service_->GetNotification(notification_id_3);
   ASSERT_TRUE(notification_3);
-  base::string16 expected_title_3 =
-      base::ASCIIToUTF16("Google Product C detected");
-  EXPECT_EQ(expected_title_3, notification_3->title());
-  base::string16 expected_message_3 =
-      base::ASCIIToUTF16(kExpectedConnectionMessage);
-  EXPECT_EQ(expected_message_3, notification_3->message());
+
+  EXPECT_EQ(expected_title(), notification_3->title());
+  EXPECT_EQ(connection_message(kProductName_3), notification_3->message());
   EXPECT_TRUE(notification_3->delegate());
 
   device_manager_.RemoveDevice(device_3);
@@ -506,12 +528,9 @@
   base::Optional<message_center::Notification> notification_1 =
       display_service_->GetNotification(notification_id_1);
   ASSERT_TRUE(notification_1);
-  base::string16 expected_title_1 =
-      base::ASCIIToUTF16("Google Product A detected");
-  EXPECT_EQ(expected_title_1, notification_1->title());
-  base::string16 expected_message_1 =
-      base::ASCIIToUTF16(kExpectedConnectionMessage);
-  EXPECT_EQ(expected_message_1, notification_1->message());
+
+  EXPECT_EQ(expected_title(), notification_1->title());
+  EXPECT_EQ(connection_message(kProductName_1), notification_1->message());
   EXPECT_TRUE(notification_1->delegate());
 
   device_manager_.AddDevice(device_2);
@@ -519,12 +538,9 @@
   base::Optional<message_center::Notification> notification_2 =
       display_service_->GetNotification(notification_id_2);
   ASSERT_TRUE(notification_2);
-  base::string16 expected_title_2 =
-      base::ASCIIToUTF16("Google Product B detected");
-  EXPECT_EQ(expected_title_2, notification_2->title());
-  base::string16 expected_message_2 =
-      base::ASCIIToUTF16(kExpectedConnectionMessage);
-  EXPECT_EQ(expected_message_2, notification_2->message());
+
+  EXPECT_EQ(expected_title(), notification_2->title());
+  EXPECT_EQ(connection_message(kProductName_2), notification_2->message());
   EXPECT_TRUE(notification_2->delegate());
 
   device_manager_.RemoveDevice(device_2);
@@ -536,12 +552,9 @@
   base::Optional<message_center::Notification> notification_3 =
       display_service_->GetNotification(notification_id_3);
   ASSERT_TRUE(notification_3);
-  base::string16 expected_title_3 =
-      base::ASCIIToUTF16("Google Product C detected");
-  EXPECT_EQ(expected_title_3, notification_3->title());
-  base::string16 expected_message_3 =
-      base::ASCIIToUTF16(kExpectedConnectionMessage);
-  EXPECT_EQ(expected_message_3, notification_3->message());
+
+  EXPECT_EQ(expected_title(), notification_3->title());
+  EXPECT_EQ(connection_message(kProductName_3), notification_3->message());
   EXPECT_TRUE(notification_3->delegate());
 
   device_manager_.RemoveDevice(device_1);
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc b/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
index b18953b..b603226 100644
--- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
+++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
@@ -672,6 +672,30 @@
   EXPECT_THAT(GetBody(), kDummyBody);
 }
 
+IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest,
+                       FallbackProxyUsedWhenBlockForLargeDurationSent) {
+  base::HistogramTester histogram_tester;
+  net::EmbeddedTestServer test_server;
+  test_server.RegisterRequestHandler(
+      base::BindRepeating(&BasicResponse, kDummyBody));
+  ASSERT_TRUE(test_server.Start());
+
+  // Sending block=86400 triggers a long bypass event that blocks requests for
+  // a day.
+  SetHeader("block=86400");
+  ui_test_utils::NavigateToURL(browser(),
+                               GetURLWithMockHost(test_server, "/echo"));
+  EXPECT_THAT(GetBody(), kDummyBody);
+  histogram_tester.ExpectUniqueSample("DataReductionProxy.BlockTypePrimary",
+                                      BYPASS_EVENT_TYPE_LONG, 1);
+
+  // Request should still not use proxy.
+  SetHeader("");
+  ui_test_utils::NavigateToURL(browser(),
+                               GetURLWithMockHost(test_server, "/echo"));
+  EXPECT_THAT(GetBody(), kDummyBody);
+}
+
 class DataReductionProxyResourceTypeBrowsertest
     : public DataReductionProxyBrowsertest {
  public:
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 91affac..67c1253 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -849,13 +849,13 @@
   },
   {
     "name": "enable-autofill-manual-fallback",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
+    "owners": [ "tmartino@chromium.org" ],
+    "expiry_milestone": 78
   },
   {
     "name": "enable-autofill-refresh-style",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
+    "owners": [ "tmartino@chromium.org" ],
+    "expiry_milestone": 78
   },
   {
     "name": "enable-autofill-save-card-dialog-unlabeled-expiration-date",
@@ -1752,6 +1752,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "enable-tab-groups",
+    "owners": [ "memex-team@google.com" ],
+    "expiry_milestone": 80
+  },
+  {
     "name": "enable-tab-switcher-on-return",
     "owners": [ "memex-team@google.com" ],
     "expiry_milestone": 76
@@ -2574,6 +2579,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "omnibox-zero-suggestions-on-ntp",
+    "omners": [ "chrome-android-omnibox-team@google.com" ],
+    "expiry_milestone": 81
+  },
+  {
     "name": "only-new-password-form-parsing",
     "owners": [ "dvadym" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index f156b15..afc8c1a5 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1450,6 +1450,11 @@
     "Always displays voice search icon in focused omnibox as long as voice "
     "search is possible";
 
+const char kOmniboxZeroSuggestionsOnNTPName[] =
+    "Omnibox Zero Suggestions on New Tab Page";
+const char kOmniboxZeroSuggestionsOnNTPDescription[] =
+    "Offer suggestions when URL bar (omnibox) is focused.";
+
 const char kOnlyNewPasswordFormParsingName[] =
     "Use only new password form parsing";
 const char kOnlyNewPasswordFormParsingDescription[] =
@@ -1854,6 +1859,10 @@
 const char kTabGridLayoutAndroidDescription[] =
     "Allows users to see their tabs in a grid layout in the tab switcher.";
 
+const char kTabGroupsAndroidName[] = "Tab Groups";
+const char kTabGroupsAndroidDescription[] =
+    "Allows users to create groups to better organize their tabs.";
+
 const char kTabGroupsName[] = "Tab Groups";
 const char kTabGroupsDescription[] =
     "Allows users to organize tabs into visually distinct groups, e.g. to "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 2cb2f0d..9a7c87ec 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -872,6 +872,9 @@
 extern const char kOmniboxVoiceSearchAlwaysVisibleName[];
 extern const char kOmniboxVoiceSearchAlwaysVisibleDescription[];
 
+extern const char kOmniboxZeroSuggestionsOnNTPName[];
+extern const char kOmniboxZeroSuggestionsOnNTPDescription[];
+
 extern const char kOnTheFlyMhtmlHashComputationName[];
 extern const char kOnTheFlyMhtmlHashComputationDescription[];
 
@@ -1098,6 +1101,9 @@
 extern const char kTabGridLayoutAndroidName[];
 extern const char kTabGridLayoutAndroidDescription[];
 
+extern const char kTabGroupsAndroidName[];
+extern const char kTabGroupsAndroidDescription[];
+
 extern const char kTabGroupsName[];
 extern const char kTabGroupsDescription[];
 
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
index 512e8af..7b6cf39 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
@@ -134,7 +134,7 @@
 }
 
 // Load given URL and verify that it loaded an interstitial and hid the URL.
-void LoadInterstitialAt(Browser* browser, const GURL& url) {
+void LoadAndCheckInterstitialAt(Browser* browser, const GURL& url) {
   content::WebContents* web_contents =
       browser->tab_strip_model()->GetActiveWebContents();
 
@@ -275,7 +275,7 @@
             browser->profile(), ServiceAccessType::EXPLICIT_ACCESS);
     ui_test_utils::WaitForHistoryToLoad(history_service);
 
-    LoadInterstitialAt(browser, navigated_url);
+    LoadAndCheckInterstitialAt(browser, navigated_url);
     SendInterstitialCommandSync(browser,
                                 SecurityInterstitialCommand::CMD_DONT_PROCEED);
     EXPECT_EQ(expected_suggested_url,
@@ -326,7 +326,7 @@
             browser->profile(), ServiceAccessType::EXPLICIT_ACCESS);
     ui_test_utils::WaitForHistoryToLoad(history_service);
 
-    LoadInterstitialAt(browser, navigated_url);
+    LoadAndCheckInterstitialAt(browser, navigated_url);
 
     // Clicking the ignore button in the interstitial should remove the
     // interstitial and navigate to the original URL.
@@ -691,11 +691,11 @@
     const GURL kNavigatedUrl =
         GetLongRedirect("goooglé.com", "example.net", "example.com");
     SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
-    LoadInterstitialAt(browser(), kNavigatedUrl);
+    LoadAndCheckInterstitialAt(browser(), kNavigatedUrl);
   }
 
-  // LoadInterstitialAt assumes there's not an interstitial already showing
-  // (since otherwise it can't be sure that the navigation caused it).
+  // LoadAndCheckInterstitialAt assumes there's not an interstitial already
+  // showing (since otherwise it can't be sure that the navigation caused it).
   NavigateToURLSync(browser(), GetURL("example.com"));
 
   {
@@ -703,7 +703,7 @@
     const GURL kNavigatedUrl =
         GetLongRedirect("example.net", "goooglé.com", "example.com");
     SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
-    LoadInterstitialAt(browser(), kNavigatedUrl);
+    LoadAndCheckInterstitialAt(browser(), kNavigatedUrl);
   }
 }
 
@@ -727,7 +727,7 @@
   const GURL navigated_url = GetURL("goooglé.com");
   const GURL subsequent_url = GetURL("example.com");
 
-  LoadInterstitialAt(browser(), navigated_url);
+  LoadAndCheckInterstitialAt(browser(), navigated_url);
   NavigateToURLSync(browser(), subsequent_url);
   CheckUkm({navigated_url}, "UserAction", UserAction::kCloseOrBack);
 }
@@ -738,7 +738,7 @@
                        UkmRecordedAfterSuggestionAccepted) {
   const GURL navigated_url = GetURL("goooglé.com");
 
-  LoadInterstitialAt(browser(), navigated_url);
+  LoadAndCheckInterstitialAt(browser(), navigated_url);
   SendInterstitialCommandSync(browser(),
                               SecurityInterstitialCommand::CMD_DONT_PROCEED);
   CheckUkm({navigated_url}, "UserAction", UserAction::kAcceptSuggestion);
@@ -750,7 +750,7 @@
                        UkmRecordedAfterSuggestionIgnored) {
   const GURL navigated_url = GetURL("goooglé.com");
 
-  LoadInterstitialAt(browser(), navigated_url);
+  LoadAndCheckInterstitialAt(browser(), navigated_url);
   SendInterstitialCommandSync(browser(),
                               SecurityInterstitialCommand::CMD_PROCEED);
   CheckUkm({navigated_url}, "UserAction", UserAction::kClickThrough);
@@ -759,7 +759,7 @@
 // Verify that the URL shows normally on pages after a lookalike interstitial.
 IN_PROC_BROWSER_TEST_F(LookalikeUrlInterstitialPageBrowserTest,
                        UrlShownAfterInterstitial) {
-  LoadInterstitialAt(browser(), GetURL("goooglé.com"));
+  LoadAndCheckInterstitialAt(browser(), GetURL("goooglé.com"));
 
   // URL should be showing again when we navigate to a normal URL
   NavigateToURLSync(browser(), GetURL("example.com"));
@@ -769,3 +769,39 @@
   NavigateToURLSync(browser(), GURL("chrome://newtab"));
   EXPECT_FALSE(IsUrlShowing(browser()));
 }
+
+// Verify that bypassing warnings in the main profile does not affect incognito.
+IN_PROC_BROWSER_TEST_F(LookalikeUrlInterstitialPageBrowserTest,
+                       MainProfileDoesNotAffectIncognito) {
+  const GURL kNavigatedUrl = GetURL("googlé.com");
+
+  // Set low engagement scores in the main profile and in incognito.
+  Browser* incognito = CreateIncognitoBrowser();
+  SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
+  SetEngagementScore(incognito, kNavigatedUrl, kLowEngagement);
+
+  LoadAndCheckInterstitialAt(browser(), kNavigatedUrl);
+  // PROCEEDing will disable the interstitial on subsequent navigations
+  SendInterstitialCommandSync(browser(),
+                              SecurityInterstitialCommand::CMD_PROCEED);
+
+  LoadAndCheckInterstitialAt(incognito, kNavigatedUrl);
+}
+
+// Verify that bypassing warnings in incognito does not affect the main profile.
+IN_PROC_BROWSER_TEST_F(LookalikeUrlInterstitialPageBrowserTest,
+                       IncognitoDoesNotAffectMainProfile) {
+  const GURL kNavigatedUrl = GetURL("googlé.com");
+
+  // Set low engagement scores in the main profile and in incognito.
+  Browser* incognito = CreateIncognitoBrowser();
+  SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
+  SetEngagementScore(incognito, kNavigatedUrl, kLowEngagement);
+
+  LoadAndCheckInterstitialAt(incognito, kNavigatedUrl);
+  // PROCEEDing will disable the interstitial on subsequent navigations
+  SendInterstitialCommandSync(incognito,
+                              SecurityInterstitialCommand::CMD_PROCEED);
+
+  LoadAndCheckInterstitialAt(browser(), kNavigatedUrl);
+}
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.cc b/chrome/browser/metrics/process_memory_metrics_emitter.cc
index c512785..fee8fe8 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter.cc
@@ -101,6 +101,9 @@
      EmitTo::kUkmAndUmaAsSize, &Memory_Experimental::SetLevelDatabase},
     {"malloc", "Malloc", kLargeMetric, kEffectiveSize, EmitTo::kUkmAndUmaAsSize,
      &Memory_Experimental::SetMalloc},
+    {"malloc/allocated_objects", "Malloc.AllocatedObjects", kLargeMetric,
+     kEffectiveSize, EmitTo::kUkmAndUmaAsSize,
+     &Memory_Experimental::SetMalloc_AllocatedObjects},
     {"mojo", "NumberOfMojoHandles", !kLargeMetric,
      MemoryAllocatorDump::kNameObjectCount, EmitTo::kUkmOnly,
      &Memory_Experimental::SetNumberOfMojoHandles},
diff --git a/chrome/browser/notifications/notification_display_service_impl.cc b/chrome/browser/notifications/notification_display_service_impl.cc
index 765345c3..33c8f59e 100644
--- a/chrome/browser/notifications/notification_display_service_impl.cc
+++ b/chrome/browser/notifications/notification_display_service_impl.cc
@@ -125,7 +125,7 @@
     AddNotificationHandler(
         NotificationHandler::Type::SEND_TAB_TO_SELF,
         std::make_unique<send_tab_to_self::DesktopNotificationHandler>(
-            profile));
+            profile_));
 #endif
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/performance_manager/graph/frame_node_impl.h b/chrome/browser/performance_manager/graph/frame_node_impl.h
index 2fb4a432..136505b 100644
--- a/chrome/browser/performance_manager/graph/frame_node_impl.h
+++ b/chrome/browser/performance_manager/graph/frame_node_impl.h
@@ -34,12 +34,6 @@
   ~FrameNodeImpl() override;
 
   // FrameNode implementation.
-  void SetProcess(
-      const resource_coordinator::CoordinationUnitID& cu_id) override;
-  void AddChildFrame(
-      const resource_coordinator::CoordinationUnitID& cu_id) override;
-  void RemoveChildFrame(
-      const resource_coordinator::CoordinationUnitID& cu_id) override;
   void SetNetworkAlmostIdle(bool idle) override;
   void SetLifecycleState(
       resource_coordinator::mojom::LifecycleState state) override;
@@ -49,6 +43,10 @@
       resource_coordinator::mojom::InterventionPolicy policy) override;
   void OnNonPersistentNotificationCreated() override;
 
+  void SetProcess(const resource_coordinator::CoordinationUnitID& cu_id);
+  void AddChildFrame(const resource_coordinator::CoordinationUnitID& cu_id);
+  void RemoveChildFrame(const resource_coordinator::CoordinationUnitID& cu_id);
+
   FrameNodeImpl* GetParentFrameNode() const;
   PageNodeImpl* GetPageNode() const;
   ProcessNodeImpl* GetProcessNode() const;
diff --git a/chrome/browser/performance_manager/graph/node_base.h b/chrome/browser/performance_manager/graph/node_base.h
index 819c97d..bcd9a9d 100644
--- a/chrome/browser/performance_manager/graph/node_base.h
+++ b/chrome/browser/performance_manager/graph/node_base.h
@@ -7,6 +7,7 @@
 
 #include <map>
 #include <memory>
+#include <utility>
 
 #include "base/callback.h"
 #include "base/macros.h"
@@ -19,7 +20,6 @@
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/resource_coordinator/public/cpp/coordination_unit_types.h"
 #include "services/resource_coordinator/public/mojom/coordination_unit.mojom.h"
-#include "services/resource_coordinator/public/mojom/coordination_unit_provider.mojom.h"
 
 namespace performance_manager {
 
@@ -98,53 +98,52 @@
   DISALLOW_COPY_AND_ASSIGN(NodeBase);
 };
 
-template <class CoordinationUnitClass,
-          class MojoInterfaceClass,
-          class MojoRequestClass>
-class CoordinationUnitInterface : public NodeBase, public MojoInterfaceClass {
+template <class NodeClass>
+class TypedNodeBase : public NodeBase {
  public:
-  static const CoordinationUnitClass* FromNodeBase(const NodeBase* cu) {
-    DCHECK(cu->id().type == CoordinationUnitClass::Type());
-    return static_cast<const CoordinationUnitClass*>(cu);
+  TypedNodeBase(const resource_coordinator::CoordinationUnitID& id,
+                Graph* graph)
+      : NodeBase(id, graph) {}
+
+  static const NodeClass* FromNodeBase(const NodeBase* cu) {
+    DCHECK(cu->id().type == NodeClass::Type());
+    return static_cast<const NodeClass*>(cu);
   }
 
-  static CoordinationUnitClass* FromNodeBase(NodeBase* cu) {
-    DCHECK(cu->id().type == CoordinationUnitClass::Type());
-    return static_cast<CoordinationUnitClass*>(cu);
+  static NodeClass* FromNodeBase(NodeBase* cu) {
+    DCHECK(cu->id().type == NodeClass::Type());
+    return static_cast<NodeClass*>(cu);
   }
 
-  CoordinationUnitInterface(const resource_coordinator::CoordinationUnitID& id,
-                            Graph* graph)
-      : NodeBase(id, graph), binding_(this) {}
-
-  ~CoordinationUnitInterface() override = default;
-
-  void Bind(MojoRequestClass request) { binding_.Bind(std::move(request)); }
-
-  void GetID(typename MojoInterfaceClass::GetIDCallback callback) override {
-    std::move(callback).Run(id_);
-  }
-  void AddBinding(MojoRequestClass request) override {
-    bindings_.AddBinding(this, std::move(request));
-  }
-
-  mojo::Binding<MojoInterfaceClass>& binding() { return binding_; }
-
-  static CoordinationUnitClass* GetNodeByID(
+  static NodeClass* GetNodeByID(
       Graph* graph,
       const resource_coordinator::CoordinationUnitID cu_id) {
-    DCHECK(cu_id.type == CoordinationUnitClass::Type());
+    DCHECK(cu_id.type == NodeClass::Type());
     auto* cu = graph->GetNodeByID(cu_id);
     if (!cu)
       return nullptr;
 
-    CHECK_EQ(cu->id().type, CoordinationUnitClass::Type());
-    return static_cast<CoordinationUnitClass*>(cu);
+    CHECK_EQ(cu->id().type, NodeClass::Type());
+    return static_cast<NodeClass*>(cu);
+  }
+};
+
+template <class NodeClass, class MojoInterfaceClass, class MojoRequestClass>
+class CoordinationUnitInterface : public TypedNodeBase<NodeClass>,
+                                  public MojoInterfaceClass {
+ public:
+  CoordinationUnitInterface(const resource_coordinator::CoordinationUnitID& id,
+                            Graph* graph)
+      : TypedNodeBase<NodeClass>(id, graph) {}
+
+  ~CoordinationUnitInterface() override = default;
+
+  void AddBinding(MojoRequestClass request) {
+    bindings_.AddBinding(this, std::move(request));
   }
 
  private:
   mojo::BindingSet<MojoInterfaceClass> bindings_;
-  mojo::Binding<MojoInterfaceClass> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(CoordinationUnitInterface);
 };
diff --git a/chrome/browser/performance_manager/graph/page_node_impl.cc b/chrome/browser/performance_manager/graph/page_node_impl.cc
index 4110968..dc4fec10 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl.cc
+++ b/chrome/browser/performance_manager/graph/page_node_impl.cc
@@ -32,7 +32,7 @@
 
 PageNodeImpl::PageNodeImpl(const resource_coordinator::CoordinationUnitID& id,
                            Graph* graph)
-    : CoordinationUnitInterface(id, graph),
+    : TypedNodeBase(id, graph),
       visibility_change_time_(ResourceCoordinatorClock::NowTicks()) {
   InvalidateAllInterventionPolicies();
 
@@ -72,7 +72,7 @@
                                 this, &is_loading_);
 }
 
-void PageNodeImpl::SetVisibility(bool is_visible) {
+void PageNodeImpl::SetIsVisible(bool is_visible) {
   SetPropertyAndNotifyObservers(&GraphObserver::OnIsVisibleChanged, is_visible,
                                 this, &is_visible_);
   // The change time needs to be updated after observers are notified, as they
diff --git a/chrome/browser/performance_manager/graph/page_node_impl.h b/chrome/browser/performance_manager/graph/page_node_impl.h
index 0963a1d3..05a0b04 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl.h
+++ b/chrome/browser/performance_manager/graph/page_node_impl.h
@@ -17,11 +17,7 @@
 class PageAlmostIdleData;
 class ProcessNodeImpl;
 
-class PageNodeImpl
-    : public CoordinationUnitInterface<
-          PageNodeImpl,
-          resource_coordinator::mojom::PageCoordinationUnit,
-          resource_coordinator::mojom::PageCoordinationUnitRequest> {
+class PageNodeImpl : public TypedNodeBase<PageNodeImpl> {
  public:
   struct PageAlmostIdleHelper;
 
@@ -33,20 +29,16 @@
                Graph* graph);
   ~PageNodeImpl() override;
 
-  // resource_coordinator::mojom::PageCoordinationUnit implementation.
-  void AddFrame(const resource_coordinator::CoordinationUnitID& cu_id) override;
-  void RemoveFrame(
-      const resource_coordinator::CoordinationUnitID& cu_id) override;
-  void SetIsLoading(bool is_loading) override;
-  // TODO(chrisha): Once this is removed from the Mojo interface, rename it
-  // SetIsVisible for consistency.
-  void SetVisibility(bool is_visible) override;
-  void SetUKMSourceId(int64_t ukm_source_id) override;
-  void OnFaviconUpdated() override;
-  void OnTitleUpdated() override;
+  void AddFrame(const resource_coordinator::CoordinationUnitID& cu_id);
+  void RemoveFrame(const resource_coordinator::CoordinationUnitID& cu_id);
+  void SetIsLoading(bool is_loading);
+  void SetIsVisible(bool is_visible);
+  void SetUKMSourceId(int64_t ukm_source_id);
+  void OnFaviconUpdated();
+  void OnTitleUpdated();
   void OnMainFrameNavigationCommitted(base::TimeTicks navigation_committed_time,
                                       int64_t navigation_id,
-                                      const std::string& url) override;
+                                      const std::string& url);
 
   // There is no direct relationship between processes and pages. However,
   // frames are accessible by both processes and frames, so we find all of the
diff --git a/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
index 62fc1530..84132f5 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
+++ b/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
@@ -141,13 +141,13 @@
 TEST_F(PageNodeImplTest, TimeSinceLastVisibilityChange) {
   MockSinglePageInSingleProcessGraph cu_graph(coordination_unit_graph());
 
-  cu_graph.page->SetVisibility(true);
+  cu_graph.page->SetIsVisible(true);
   EXPECT_TRUE(cu_graph.page->is_visible());
   AdvanceClock(base::TimeDelta::FromSeconds(42));
   EXPECT_EQ(base::TimeDelta::FromSeconds(42),
             cu_graph.page->TimeSinceLastVisibilityChange());
 
-  cu_graph.page->SetVisibility(false);
+  cu_graph.page->SetIsVisible(false);
   AdvanceClock(base::TimeDelta::FromSeconds(23));
   EXPECT_EQ(base::TimeDelta::FromSeconds(23),
             cu_graph.page->TimeSinceLastVisibilityChange());
diff --git a/chrome/browser/performance_manager/graph/process_node_impl.h b/chrome/browser/performance_manager/graph/process_node_impl.h
index 827aede..6fd2ab2d 100644
--- a/chrome/browser/performance_manager/graph/process_node_impl.h
+++ b/chrome/browser/performance_manager/graph/process_node_impl.h
@@ -42,14 +42,15 @@
   ~ProcessNodeImpl() override;
 
   // resource_coordinator::mojom::ProcessCoordinationUnit implementation.
-  void SetCPUUsage(double cpu_usage) override;
   void SetExpectedTaskQueueingDuration(base::TimeDelta duration) override;
-  void SetLaunchTime(base::Time launch_time) override;
   void SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low) override;
-  void SetPID(base::ProcessId pid) override;
-  void SetProcessExitStatus(int32_t exit_status) override;
   void OnRendererIsBloated() override;
 
+  void SetCPUUsage(double cpu_usage);
+  void SetLaunchTime(base::Time launch_time);
+  void SetPID(base::ProcessId pid);
+  void SetProcessExitStatus(int32_t exit_status);
+
   // Private implementation properties.
   void set_private_footprint_kb(uint64_t private_footprint_kb) {
     private_footprint_kb_ = private_footprint_kb;
diff --git a/chrome/browser/performance_manager/graph/system_node_impl.cc b/chrome/browser/performance_manager/graph/system_node_impl.cc
index 3496179..1a6e3e2 100644
--- a/chrome/browser/performance_manager/graph/system_node_impl.cc
+++ b/chrome/browser/performance_manager/graph/system_node_impl.cc
@@ -16,10 +16,14 @@
 
 namespace performance_manager {
 
+ProcessResourceMeasurement::ProcessResourceMeasurement() = default;
+ProcessResourceMeasurementBatch::ProcessResourceMeasurementBatch() = default;
+ProcessResourceMeasurementBatch::~ProcessResourceMeasurementBatch() = default;
+
 SystemNodeImpl::SystemNodeImpl(
     const resource_coordinator::CoordinationUnitID& id,
     Graph* graph)
-    : CoordinationUnitInterface(id, graph) {}
+    : TypedNodeBase(id, graph) {}
 
 SystemNodeImpl::~SystemNodeImpl() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -30,8 +34,7 @@
 }
 
 void SystemNodeImpl::DistributeMeasurementBatch(
-    resource_coordinator::mojom::ProcessResourceMeasurementBatchPtr
-        measurement_batch) {
+    std::unique_ptr<ProcessResourceMeasurementBatch> measurement_batch) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::TimeDelta time_since_last_measurement;
   if (!last_measurement_end_time_.is_null()) {
@@ -60,10 +63,10 @@
   std::set<PageNodeImpl*> pages;
   std::vector<ProcessNodeImpl*> found_processes;
   for (const auto& measurement : measurement_batch->measurements) {
-    ProcessNodeImpl* process = graph()->GetProcessNodeByPid(measurement->pid);
+    ProcessNodeImpl* process = graph()->GetProcessNodeByPid(measurement.pid);
     if (process) {
       base::TimeDelta cumulative_cpu_delta =
-          measurement->cpu_usage - process->cumulative_cpu_usage();
+          measurement.cpu_usage - process->cumulative_cpu_usage();
       DCHECK_LE(base::TimeDelta(), cumulative_cpu_delta);
 
       // Distribute the CPU delta to the pages that own the frames in this
@@ -109,7 +112,7 @@
       }
       process->set_cumulative_cpu_usage(process->cumulative_cpu_usage() +
                                         cumulative_cpu_delta);
-      process->set_private_footprint_kb(measurement->private_footprint_kb);
+      process->set_private_footprint_kb(measurement.private_footprint_kb);
 
       // Note the found processes.
       found_processes.push_back(process);
diff --git a/chrome/browser/performance_manager/graph/system_node_impl.h b/chrome/browser/performance_manager/graph/system_node_impl.h
index 0bee680..233d763 100644
--- a/chrome/browser/performance_manager/graph/system_node_impl.h
+++ b/chrome/browser/performance_manager/graph/system_node_impl.h
@@ -5,17 +5,47 @@
 #ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_GRAPH_SYSTEM_NODE_IMPL_H_
 #define CHROME_BROWSER_PERFORMANCE_MANAGER_GRAPH_SYSTEM_NODE_IMPL_H_
 
+#include <cstdint>
+#include <memory>
+#include <vector>
+
 #include "base/macros.h"
+#include "base/process/process_handle.h"
 #include "base/time/time.h"
 #include "chrome/browser/performance_manager/graph/node_base.h"
 
 namespace performance_manager {
 
-class SystemNodeImpl
-    : public CoordinationUnitInterface<
-          SystemNodeImpl,
-          resource_coordinator::mojom::SystemCoordinationUnit,
-          resource_coordinator::mojom::SystemCoordinationUnitRequest> {
+// TODO(siggi): In the end game, this should be private implementation detail
+//     of the performance measurement graph decorator. It's here for now because
+//     there's still a thread hop to get the measurement results into the graph.
+struct ProcessResourceMeasurement {
+  ProcessResourceMeasurement();
+
+  // Identifies the process associated with this measurement.
+  base::ProcessId pid;
+
+  // The cumulative CPU usage accrued to this process from its start.
+  base::TimeDelta cpu_usage;
+
+  // The private memory footprint of the process.
+  uint32_t private_footprint_kb = 0;
+};
+
+struct ProcessResourceMeasurementBatch {
+  ProcessResourceMeasurementBatch();
+  ~ProcessResourceMeasurementBatch();
+
+  // These times bracket the capture of the entire dump, e.g. each distinct
+  // measurement is captured somewhere between |batch_started_time| and
+  // |batch_ended_time|.
+  base::TimeTicks batch_started_time;
+  base::TimeTicks batch_ended_time;
+
+  std::vector<ProcessResourceMeasurement> measurements;
+};
+
+class SystemNodeImpl : public TypedNodeBase<SystemNodeImpl> {
  public:
   static constexpr resource_coordinator::CoordinationUnitType Type() {
     return resource_coordinator::CoordinationUnitType::kSystem;
@@ -25,11 +55,9 @@
                  Graph* graph);
   ~SystemNodeImpl() override;
 
-  // resource_coordinator::mojom::SystemCoordinationUnit implementation:
-  void OnProcessCPUUsageReady() override;
+  void OnProcessCPUUsageReady();
   void DistributeMeasurementBatch(
-      resource_coordinator::mojom::ProcessResourceMeasurementBatchPtr
-          measurement_batch) override;
+      std::unique_ptr<ProcessResourceMeasurementBatch> measurement_batch);
 
   // Accessors for the start/end times bracketing when the last performance
   // measurement occurred.
diff --git a/chrome/browser/performance_manager/graph/system_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/system_node_impl_unittest.cc
index 12f4a83..d221cf0 100644
--- a/chrome/browser/performance_manager/graph/system_node_impl_unittest.cc
+++ b/chrome/browser/performance_manager/graph/system_node_impl_unittest.cc
@@ -70,24 +70,23 @@
   base::SimpleTestTickClock clock_;
 };
 
-resource_coordinator::mojom::ProcessResourceMeasurementBatchPtr
-CreateMeasurementBatch(base::TimeTicks start_end_time,
-                       size_t num_processes,
-                       base::TimeDelta additional_cpu_time) {
-  resource_coordinator::mojom::ProcessResourceMeasurementBatchPtr batch =
-      resource_coordinator::mojom::ProcessResourceMeasurementBatch::New();
+std::unique_ptr<ProcessResourceMeasurementBatch> CreateMeasurementBatch(
+    base::TimeTicks start_end_time,
+    size_t num_processes,
+    base::TimeDelta additional_cpu_time) {
+  std::unique_ptr<ProcessResourceMeasurementBatch> batch =
+      std::make_unique<ProcessResourceMeasurementBatch>();
   batch->batch_started_time = start_end_time;
   batch->batch_ended_time = start_end_time;
 
   for (size_t i = 1; i <= num_processes; ++i) {
-    resource_coordinator::mojom::ProcessResourceMeasurementPtr measurement =
-        resource_coordinator::mojom::ProcessResourceMeasurement::New();
-    measurement->pid = i;
-    measurement->cpu_usage =
+    ProcessResourceMeasurement measurement;
+    measurement.pid = i;
+    measurement.cpu_usage =
         base::TimeDelta::FromMicroseconds(i * 10) + additional_cpu_time;
-    measurement->private_footprint_kb = static_cast<uint32_t>(i * 100);
+    measurement.private_footprint_kb = static_cast<uint32_t>(i * 100);
 
-    batch->measurements.push_back(std::move(measurement));
+    batch->measurements.push_back(measurement);
   }
 
   return batch;
diff --git a/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc b/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc
index 5aa8311..7b6d7d5 100644
--- a/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc
+++ b/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc
@@ -65,13 +65,13 @@
                                           kDummyID, kDummyUrl);
   AdvanceClock(kTestMetricsReportDelayTimeout);
 
-  page_cu->SetVisibility(true);
+  page_cu->SetIsVisible(true);
   page_cu->OnTitleUpdated();
   // The page is not backgrounded, thus no metrics recorded.
   histogram_tester_.ExpectTotalCount(kTabFromBackgroundedToFirstTitleUpdatedUMA,
                                      0);
 
-  page_cu->SetVisibility(false);
+  page_cu->SetIsVisible(false);
   page_cu->OnTitleUpdated();
   // The page is backgrounded, thus metrics recorded.
   histogram_tester_.ExpectTotalCount(kTabFromBackgroundedToFirstTitleUpdatedUMA,
@@ -82,8 +82,8 @@
   histogram_tester_.ExpectTotalCount(kTabFromBackgroundedToFirstTitleUpdatedUMA,
                                      1);
 
-  page_cu->SetVisibility(true);
-  page_cu->SetVisibility(false);
+  page_cu->SetIsVisible(true);
+  page_cu->SetIsVisible(false);
   page_cu->OnTitleUpdated();
   // The page is backgrounded from foregrounded, thus metrics recorded.
   histogram_tester_.ExpectTotalCount(kTabFromBackgroundedToFirstTitleUpdatedUMA,
@@ -96,7 +96,7 @@
 
   page_cu->OnMainFrameNavigationCommitted(ResourceCoordinatorClock::NowTicks(),
                                           kDummyID, kDummyUrl);
-  page_cu->SetVisibility(false);
+  page_cu->SetIsVisible(false);
   page_cu->OnTitleUpdated();
   // The page is within 5 minutes after main frame navigation was committed,
   // thus no metrics recorded.
@@ -118,13 +118,13 @@
                                           kDummyID, kDummyUrl);
   AdvanceClock(kTestMetricsReportDelayTimeout);
 
-  page_cu->SetVisibility(true);
+  page_cu->SetIsVisible(true);
   frame_cu->OnNonPersistentNotificationCreated();
   // The page is not backgrounded, thus no metrics recorded.
   histogram_tester_.ExpectTotalCount(
       kTabFromBackgroundedToFirstNonPersistentNotificationCreatedUMA, 0);
 
-  page_cu->SetVisibility(false);
+  page_cu->SetIsVisible(false);
   frame_cu->OnNonPersistentNotificationCreated();
   // The page is backgrounded, thus metrics recorded.
   histogram_tester_.ExpectTotalCount(
@@ -135,8 +135,8 @@
   histogram_tester_.ExpectTotalCount(
       kTabFromBackgroundedToFirstNonPersistentNotificationCreatedUMA, 1);
 
-  page_cu->SetVisibility(true);
-  page_cu->SetVisibility(false);
+  page_cu->SetIsVisible(true);
+  page_cu->SetIsVisible(false);
   frame_cu->OnNonPersistentNotificationCreated();
   // The page is backgrounded from foregrounded, thus metrics recorded.
   histogram_tester_.ExpectTotalCount(
@@ -152,7 +152,7 @@
 
   page_cu->OnMainFrameNavigationCommitted(ResourceCoordinatorClock::NowTicks(),
                                           kDummyID, kDummyUrl);
-  page_cu->SetVisibility(false);
+  page_cu->SetIsVisible(false);
   frame_cu->OnNonPersistentNotificationCreated();
   // The page is within 5 minutes after main frame navigation was committed,
   // thus no metrics recorded.
@@ -171,13 +171,13 @@
                                           kDummyID, kDummyUrl);
   AdvanceClock(kTestMetricsReportDelayTimeout);
 
-  page_cu->SetVisibility(true);
+  page_cu->SetIsVisible(true);
   page_cu->OnFaviconUpdated();
   // The page is not backgrounded, thus no metrics recorded.
   histogram_tester_.ExpectTotalCount(
       kTabFromBackgroundedToFirstFaviconUpdatedUMA, 0);
 
-  page_cu->SetVisibility(false);
+  page_cu->SetIsVisible(false);
   page_cu->OnFaviconUpdated();
   // The page is backgrounded, thus metrics recorded.
   histogram_tester_.ExpectTotalCount(
@@ -188,8 +188,8 @@
   histogram_tester_.ExpectTotalCount(
       kTabFromBackgroundedToFirstFaviconUpdatedUMA, 1);
 
-  page_cu->SetVisibility(true);
-  page_cu->SetVisibility(false);
+  page_cu->SetIsVisible(true);
+  page_cu->SetIsVisible(false);
   page_cu->OnFaviconUpdated();
   // The page is backgrounded from foregrounded, thus metrics recorded.
   histogram_tester_.ExpectTotalCount(
@@ -202,7 +202,7 @@
 
   page_cu->OnMainFrameNavigationCommitted(ResourceCoordinatorClock::NowTicks(),
                                           kDummyID, kDummyUrl);
-  page_cu->SetVisibility(false);
+  page_cu->SetIsVisible(false);
   page_cu->OnFaviconUpdated();
   // The page is within 5 minutes after main frame navigation was committed,
   // thus no metrics recorded.
diff --git a/chrome/browser/performance_manager/observers/page_signal_generator_impl_unittest.cc b/chrome/browser/performance_manager/observers/page_signal_generator_impl_unittest.cc
index 152cc843..db96837 100644
--- a/chrome/browser/performance_manager/observers/page_signal_generator_impl_unittest.cc
+++ b/chrome/browser/performance_manager/observers/page_signal_generator_impl_unittest.cc
@@ -210,21 +210,20 @@
 
 namespace {
 
-resource_coordinator::mojom::ProcessResourceMeasurementBatchPtr
-CreateMeasurementBatch(base::TimeTicks start_time,
-                       size_t cpu_time_us,
-                       size_t private_fp_kb) {
-  resource_coordinator::mojom::ProcessResourceMeasurementBatchPtr batch =
-      resource_coordinator::mojom::ProcessResourceMeasurementBatch::New();
+std::unique_ptr<ProcessResourceMeasurementBatch> CreateMeasurementBatch(
+    base::TimeTicks start_time,
+    size_t cpu_time_us,
+    size_t private_fp_kb) {
+  std::unique_ptr<ProcessResourceMeasurementBatch> batch =
+      std::make_unique<ProcessResourceMeasurementBatch>();
   batch->batch_started_time = start_time;
   batch->batch_ended_time = start_time + base::TimeDelta::FromMicroseconds(10);
 
-  resource_coordinator::mojom::ProcessResourceMeasurementPtr measurement =
-      resource_coordinator::mojom::ProcessResourceMeasurement::New();
-  measurement->pid = 1;
-  measurement->cpu_usage = base::TimeDelta::FromMicroseconds(cpu_time_us);
-  measurement->private_footprint_kb = static_cast<uint32_t>(private_fp_kb);
-  batch->measurements.push_back(std::move(measurement));
+  ProcessResourceMeasurement measurement;
+  measurement.pid = 1;
+  measurement.cpu_usage = base::TimeDelta::FromMicroseconds(cpu_time_us);
+  measurement.private_footprint_kb = static_cast<uint32_t>(private_fp_kb);
+  batch->measurements.push_back(measurement);
 
   return batch;
 }
diff --git a/chrome/browser/performance_manager/performance_manager.cc b/chrome/browser/performance_manager/performance_manager.cc
index e1523e4..21c045e 100644
--- a/chrome/browser/performance_manager/performance_manager.cc
+++ b/chrome/browser/performance_manager/performance_manager.cc
@@ -69,7 +69,7 @@
 }
 
 void PerformanceManager::DistributeMeasurementBatch(
-    resource_coordinator::mojom::ProcessResourceMeasurementBatchPtr batch) {
+    std::unique_ptr<ProcessResourceMeasurementBatch> batch) {
   task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&PerformanceManager::DistributeMeasurementBatchImpl,
@@ -173,7 +173,7 @@
 }
 
 void PerformanceManager::DistributeMeasurementBatchImpl(
-    resource_coordinator::mojom::ProcessResourceMeasurementBatchPtr batch) {
+    std::unique_ptr<ProcessResourceMeasurementBatch> batch) {
   SystemNodeImpl* system_node = graph_.FindOrCreateSystemNode();
   DCHECK(system_node);
 
diff --git a/chrome/browser/performance_manager/performance_manager.h b/chrome/browser/performance_manager/performance_manager.h
index 709cf40..e71899f 100644
--- a/chrome/browser/performance_manager/performance_manager.h
+++ b/chrome/browser/performance_manager/performance_manager.h
@@ -17,7 +17,6 @@
 #include "chrome/browser/performance_manager/performance_manager.h"
 #include "chrome/browser/performance_manager/webui_graph_dump_impl.h"
 #include "services/resource_coordinator/public/mojom/coordination_unit.mojom.h"
-#include "services/resource_coordinator/public/mojom/coordination_unit_provider.mojom.h"
 #include "services/service_manager/public/cpp/bind_source_info.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/connector.h"
@@ -29,6 +28,7 @@
 namespace performance_manager {
 
 class PageNodeImpl;
+struct ProcessResourceMeasurementBatch;
 
 // The performance manager is a rendezvous point for binding to performance
 // manager interfaces.
@@ -60,7 +60,7 @@
   // which will soon go away as the performance measurement moves to the
   // performance sequence.
   void DistributeMeasurementBatch(
-      resource_coordinator::mojom::ProcessResourceMeasurementBatchPtr batch);
+      std::unique_ptr<ProcessResourceMeasurementBatch> batch);
 
   // Creates a new node of the requested type and adds it to the graph.
   // May be called from any sequence.
@@ -98,7 +98,7 @@
   void BindInterfaceImpl(const std::string& interface_name,
                          mojo::ScopedMessagePipeHandle message_pipe);
   void DistributeMeasurementBatchImpl(
-      resource_coordinator::mojom::ProcessResourceMeasurementBatchPtr batch);
+      std::unique_ptr<ProcessResourceMeasurementBatch> batch);
 
   void BindWebUIGraphDump(
       resource_coordinator::mojom::WebUIGraphDumpRequest request,
diff --git a/chrome/browser/performance_manager/performance_manager_tab_helper.cc b/chrome/browser/performance_manager/performance_manager_tab_helper.cc
index debc45d2..bd0455e 100644
--- a/chrome/browser/performance_manager/performance_manager_tab_helper.cc
+++ b/chrome/browser/performance_manager/performance_manager_tab_helper.cc
@@ -247,7 +247,7 @@
   const bool is_visible = visibility != content::Visibility::HIDDEN;
   performance_manager_->task_runner()->PostTask(
       FROM_HERE,
-      base::BindOnce(&PageNodeImpl::SetVisibility,
+      base::BindOnce(&PageNodeImpl::SetIsVisible,
                      base::Unretained(page_node_.get()), is_visible));
 }
 
diff --git a/chrome/browser/previews/previews_ui_tab_helper_unittest.cc b/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
index 85cfa11..17f31c1 100644
--- a/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
+++ b/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
@@ -143,7 +143,7 @@
   std::unique_ptr<content::MockNavigationHandle> test_handle_;
 };
 
-TEST_F(PreviewsUITabHelperUnitTest, DidFinishNavigationCreatesLitePageInfoBar) {
+TEST_F(PreviewsUITabHelperUnitTest, DidFinishNavigationDisplaysUI) {
   PreviewsUITabHelper* ui_tab_helper =
       PreviewsUITabHelper::FromWebContents(web_contents());
   EXPECT_FALSE(ui_tab_helper->displayed_preview_ui());
@@ -153,7 +153,9 @@
   CallDidFinishNavigation();
   base::RunLoop().RunUntilIdle();
 
+#if !defined(OS_ANDROID)
   EXPECT_EQ(1U, infobar_service()->infobar_count());
+#endif
   EXPECT_TRUE(ui_tab_helper->displayed_preview_ui());
 
   // Navigate to reset the displayed state.
@@ -187,7 +189,7 @@
 #endif
 
 TEST_F(PreviewsUITabHelperUnitTest,
-       DidFinishNavigationCreatesNoScriptPreviewsInfoBar) {
+       DidFinishNavigationCreatesNoScriptPreviewsUI) {
   PreviewsUITabHelper* ui_tab_helper =
       PreviewsUITabHelper::FromWebContents(web_contents());
   EXPECT_FALSE(ui_tab_helper->displayed_preview_ui());
@@ -197,7 +199,9 @@
   CallDidFinishNavigation();
   base::RunLoop().RunUntilIdle();
 
+#if !defined(OS_ANDROID)
   EXPECT_EQ(1U, infobar_service()->infobar_count());
+#endif
   EXPECT_TRUE(ui_tab_helper->displayed_preview_ui());
 
   // Navigate to reset the displayed state.
@@ -285,7 +289,7 @@
 }
 
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
-TEST_F(PreviewsUITabHelperUnitTest, CreateOfflineInfoBar) {
+TEST_F(PreviewsUITabHelperUnitTest, CreateOfflineUI) {
   PreviewsUITabHelper* ui_tab_helper =
       PreviewsUITabHelper::FromWebContents(web_contents());
   EXPECT_FALSE(ui_tab_helper->displayed_preview_ui());
@@ -323,7 +327,9 @@
   CallDidFinishNavigation();
   base::RunLoop().RunUntilIdle();
 
+#if !defined(OS_ANDROID)
   EXPECT_EQ(1U, infobar_service()->infobar_count());
+#endif
   EXPECT_TRUE(ui_tab_helper->displayed_preview_ui());
 
   // Navigate to reset the displayed state.
diff --git a/chrome/browser/resource_coordinator/render_process_probe.cc b/chrome/browser/resource_coordinator/render_process_probe.cc
index b268363..8d3eea04b 100644
--- a/chrome/browser/resource_coordinator/render_process_probe.cc
+++ b/chrome/browser/resource_coordinator/render_process_probe.cc
@@ -10,6 +10,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/task/post_task.h"
 #include "build/build_config.h"
+#include "chrome/browser/performance_manager/graph/system_node_impl.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
@@ -102,22 +103,20 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(is_gathering_);
   // Create the measurement batch.
-  mojom::ProcessResourceMeasurementBatchPtr batch =
-      mojom::ProcessResourceMeasurementBatch::New();
+  std::unique_ptr<performance_manager::ProcessResourceMeasurementBatch> batch =
+      std::make_unique<performance_manager::ProcessResourceMeasurementBatch>();
 
   // Start by adding the render process hosts we know about to the batch.
   for (const auto& render_process_info_map_entry : render_process_info_map_) {
     auto& render_process_info = render_process_info_map_entry.second;
     // TODO(oysteine): Move the multiplier used to avoid precision loss
     // into a shared location, when this property gets used.
-    mojom::ProcessResourceMeasurementPtr measurement =
-        mojom::ProcessResourceMeasurement::New();
-
-    measurement->pid =
+    performance_manager::ProcessResourceMeasurement measurement;
+    measurement.pid =
         GetProcessId(render_process_info_map_entry.first, render_process_info);
-    measurement->cpu_usage = render_process_info.cpu_usage;
+    measurement.cpu_usage = render_process_info.cpu_usage;
 
-    batch->measurements.push_back(std::move(measurement));
+    batch->measurements.push_back(measurement);
   }
 
   // Record the overall outcome of the measurement.
@@ -144,8 +143,8 @@
       base::ProcessId pid = dump_entry.pid();
 
       bool used_entry = false;
-      for (const auto& measurement : batch->measurements) {
-        if (measurement->pid != pid)
+      for (auto& measurement : batch->measurements) {
+        if (measurement.pid != pid)
           continue;
 
         used_entry = true;
@@ -155,7 +154,7 @@
         DCHECK_LT(0u, num_non_measured_processes);
         --num_non_measured_processes;
 
-        measurement->private_footprint_kb =
+        measurement.private_footprint_kb =
             dump_entry.os_dump().private_footprint_kb;
         break;
       }
@@ -197,7 +196,8 @@
 }
 
 void RenderProcessProbeImpl::FinishCollectionOnUIThread(
-    mojom::ProcessResourceMeasurementBatchPtr batch) {
+    std::unique_ptr<performance_manager::ProcessResourceMeasurementBatch>
+        batch) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(is_gathering_);
   is_gathering_ = false;
@@ -254,7 +254,8 @@
 }
 
 void RenderProcessProbeImpl::DispatchMetricsOnUIThread(
-    mojom::ProcessResourceMeasurementBatchPtr batch) {
+    std::unique_ptr<performance_manager::ProcessResourceMeasurementBatch>
+        batch) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   performance_manager::PerformanceManager* performance_manager =
       performance_manager::PerformanceManager::GetInstance();
diff --git a/chrome/browser/resource_coordinator/render_process_probe.h b/chrome/browser/resource_coordinator/render_process_probe.h
index 02679a70..64a6114 100644
--- a/chrome/browser/resource_coordinator/render_process_probe.h
+++ b/chrome/browser/resource_coordinator/render_process_probe.h
@@ -17,6 +17,10 @@
 #include "chrome/browser/performance_manager/performance_manager.h"
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
 
+namespace performance_manager {
+struct ProcessResourceMeasurementBatch;
+}  // namespace performance_manager
+
 namespace resource_coordinator {
 
 // |RenderProcessProbe| collects measurements about render
@@ -86,7 +90,8 @@
       std::unique_ptr<memory_instrumentation::GlobalMemoryDump> dump);
   // (4) Finish the collection cycle on the UI thread.
   void FinishCollectionOnUIThread(
-      mojom::ProcessResourceMeasurementBatchPtr batch);
+      std::unique_ptr<performance_manager::ProcessResourceMeasurementBatch>
+          batch);
 
   // Test seams.
   virtual void RegisterRenderProcesses();
@@ -97,7 +102,8 @@
   // Dispatch the collected metrics.
   // Virtual for testing.
   virtual void DispatchMetricsOnUIThread(
-      mojom::ProcessResourceMeasurementBatchPtr batch);
+      std::unique_ptr<performance_manager::ProcessResourceMeasurementBatch>
+          batch);
 
   // A map of currently running render process host IDs to process.
   // This map is accessed alternatively from the UI thread and the IO thread,
diff --git a/chrome/browser/resource_coordinator/render_process_probe_browsertest.cc b/chrome/browser/resource_coordinator/render_process_probe_browsertest.cc
index 972dcaa..5a3520b2 100644
--- a/chrome/browser/resource_coordinator/render_process_probe_browsertest.cc
+++ b/chrome/browser/resource_coordinator/render_process_probe_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/performance_manager/graph/system_node_impl.h"
 #include "chrome/browser/resource_coordinator/render_process_probe.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -34,7 +35,8 @@
   ~TestingRenderProcessProbe() override = default;
 
   void DispatchMetricsOnUIThread(
-      mojom::ProcessResourceMeasurementBatchPtr batch) override {
+      std::unique_ptr<performance_manager::ProcessResourceMeasurementBatch>
+          batch) override {
     last_measurement_batch_ = std::move(batch);
 
     current_run_loop_->QuitWhenIdle();
@@ -54,9 +56,9 @@
     return true;
   }
 
-  const mojom::ProcessResourceMeasurementBatchPtr& last_measurement_batch()
+  performance_manager::ProcessResourceMeasurementBatch* last_measurement_batch()
       const {
-    return last_measurement_batch_;
+    return last_measurement_batch_.get();
   }
 
   const RenderProcessInfoMap& render_process_info_map() const {
@@ -82,7 +84,8 @@
  private:
   base::RunLoop* current_run_loop_ = nullptr;
 
-  mojom::ProcessResourceMeasurementBatchPtr last_measurement_batch_;
+  std::unique_ptr<performance_manager::ProcessResourceMeasurementBatch>
+      last_measurement_batch_;
 
   DISALLOW_COPY_AND_ASSIGN(TestingRenderProcessProbe);
 };
@@ -95,9 +98,9 @@
   ~RenderProcessProbeBrowserTest() override = default;
 
   static bool AtLeastOneMemoryMeasurementIsNonZero(
-      const mojom::ProcessResourceMeasurementBatchPtr& batch) {
+      const performance_manager::ProcessResourceMeasurementBatch* batch) {
     for (const auto& measurement : batch->measurements) {
-      if (measurement->private_footprint_kb > 0)
+      if (measurement.private_footprint_kb > 0)
         return true;
     }
 
@@ -134,10 +137,10 @@
   // be zero due to the measurement granularity of the OS.
   std::map<uint32_t, base::TimeDelta> cpu_usage_map;
   for (const auto& measurement : probe.last_measurement_batch()->measurements) {
-    EXPECT_LE(base::TimeDelta(), measurement->cpu_usage);
+    EXPECT_LE(base::TimeDelta(), measurement.cpu_usage);
     EXPECT_TRUE(
         cpu_usage_map
-            .insert(std::make_pair(measurement->pid, measurement->cpu_usage))
+            .insert(std::make_pair(measurement.pid, measurement.cpu_usage))
             .second);
   }
 
@@ -173,8 +176,8 @@
   // Verify that CPU usage is monotonically increasing, though the measurement
   // granulatity is such on some OSes that a zero difference is almost certain.
   for (const auto& measurement : probe.last_measurement_batch()->measurements) {
-    if (cpu_usage_map.find(measurement->pid) != cpu_usage_map.end()) {
-      EXPECT_LE(cpu_usage_map[measurement->pid], measurement->cpu_usage);
+    if (cpu_usage_map.find(measurement.pid) != cpu_usage_map.end()) {
+      EXPECT_LE(cpu_usage_map[measurement.pid], measurement.cpu_usage);
     }
   }
 
diff --git a/chrome/browser/resources/about_flash.html b/chrome/browser/resources/about_flash.html
index da4f5f6..cd19013 100644
--- a/chrome/browser/resources/about_flash.html
+++ b/chrome/browser/resources/about_flash.html
@@ -27,9 +27,7 @@
     </table>
   </div>
 </div>
-<script src="chrome://resources/js/load_time_data.js"></script>
 <script src="chrome://flash/about_flash.js"></script>
-<script src="chrome://flash/strings.js"></script>
 <script src="chrome://resources/js/jstemplate_compiled.js"></script>
 <script src="chrome://resources/js/util.js"></script>
 </body>
diff --git a/chrome/browser/resources/about_nacl/about_nacl.html b/chrome/browser/resources/about_nacl/about_nacl.html
index d492adb..a67298c4 100644
--- a/chrome/browser/resources/about_nacl/about_nacl.html
+++ b/chrome/browser/resources/about_nacl/about_nacl.html
@@ -20,9 +20,7 @@
 </div>
 <script src="chrome://resources/js/promise_resolver.js"></script>
 <script src="chrome://resources/js/cr.js"></script>
-<script src="chrome://resources/js/load_time_data.js"></script>
 <script src="chrome://nacl/about_nacl.js"></script>
-<script src="chrome://nacl/strings.js"></script>
 <script src="chrome://resources/js/jstemplate_compiled.js"></script>
 <script src="chrome://resources/js/util.js"></script>
 </body>
diff --git a/chrome/browser/resources/accessibility/accessibility.html b/chrome/browser/resources/accessibility/accessibility.html
index a755471c..478c0c27 100644
--- a/chrome/browser/resources/accessibility/accessibility.html
+++ b/chrome/browser/resources/accessibility/accessibility.html
@@ -11,10 +11,8 @@
   <link rel="stylesheet" href="chrome://resources/css/chrome_shared.css">
   <link rel="stylesheet" href="accessibility.css">
   <script src="chrome://resources/js/cr.js"></script>
-  <script src="chrome://resources/js/load_time_data.js"></script>
   <script src="chrome://resources/js/action_link.js"></script>
   <script src="chrome://resources/js/util.js"></script>
-  <script src="strings.js"></script>
   <script src="accessibility.js"></script>
 </head>
 <body>
diff --git a/chrome/browser/resources/chromeos/BUILD.gn b/chrome/browser/resources/chromeos/BUILD.gn
index 8f349ca..88dd640 100644
--- a/chrome/browser/resources/chromeos/BUILD.gn
+++ b/chrome/browser/resources/chromeos/BUILD.gn
@@ -29,6 +29,7 @@
     "internet_detail_dialog:closure_compile",
     "kiosk_next_home:closure_compile",
     "login:closure_compile",
+    "md_set_time:closure_compile",
     "multidevice_setup:closure_compile",
     "network_ui:closure_compile",
     "select_to_speak:closure_compile",
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.js
index 0d0b886..de83f6fd 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.js
@@ -104,6 +104,14 @@
    */
   addSettingZippy: function(zippy_data) {
     assert(zippy_data.length <= 3);
+
+    if (this.settingZippyLoaded_) {
+      if (this.consentStringLoaded_) {
+        this.onPageLoaded();
+      }
+      return;
+    }
+
     for (var i in zippy_data) {
       var data = zippy_data[i];
       var zippy = document.createElement('setting-zippy');
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.js
index c98b609..494d4bb1 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.js
@@ -130,6 +130,13 @@
    * Add a setting zippy with the provided data.
    */
   addSettingZippy: function(zippy_data) {
+    if (this.settingZippyLoaded_) {
+      if (this.consentStringLoaded_) {
+        this.onPageLoaded();
+      }
+      return;
+    }
+
     for (var i in zippy_data) {
       var data = zippy_data[i];
       var zippy = document.createElement('setting-zippy');
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.js
index af5cc79..99c7a07 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.js
@@ -262,6 +262,13 @@
    * Add a setting zippy with the provided data.
    */
   addSettingZippy: function(zippy_data) {
+    if (this.settingZippyLoaded_) {
+      if (this.webViewLoaded_ && this.consentStringLoaded_) {
+        this.onPageLoaded();
+      }
+      return;
+    }
+
     for (var i in zippy_data) {
       var data = zippy_data[i];
       var zippy = document.createElement('setting-zippy');
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.css b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.css
index bf420c0f..22b62e9 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.css
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.css
@@ -24,6 +24,14 @@
   text-align: center;
 }
 
+#already-setup-video-container {
+  text-align: center;
+}
+
+#already-setup-video {
+  height: 450px;
+}
+
 #already-setup-img {
   display: block;
   margin: auto;
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html
index 92a3cce..b4c5111 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html
@@ -65,7 +65,11 @@
                 i18n-content="assistantVoiceMatchAlreadySetupTitle"></div>
             <div class="content"
                 i18n-content="assistantVoiceMatchAlreadySetupMessage"></div>
-            <img id="already-setup-img" src="voice_already_setup.png">
+            <div id="already-setup-video-container">
+              <video id="already-setup-video" muted autoplay loop>
+                <source src="voice_already_setup.webm" type="video/webm">
+              </video>
+            </div>
           </div>
         </div>
       </div>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/voice_already_setup.png b/chrome/browser/resources/chromeos/assistant_optin/voice_already_setup.png
deleted file mode 100644
index b276b58a..0000000
--- a/chrome/browser/resources/chromeos/assistant_optin/voice_already_setup.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/chromeos/assistant_optin/voice_already_setup.webm b/chrome/browser/resources/chromeos/assistant_optin/voice_already_setup.webm
new file mode 100644
index 0000000..53774cd5
--- /dev/null
+++ b/chrome/browser/resources/chromeos/assistant_optin/voice_already_setup.webm
Binary files differ
diff --git a/chrome/browser/resources/chromeos/login/md_login.js b/chrome/browser/resources/chromeos/login/md_login.js
index a03c337..b6a389e 100644
--- a/chrome/browser/resources/chromeos/login/md_login.js
+++ b/chrome/browser/resources/chromeos/login/md_login.js
@@ -113,9 +113,6 @@
       cr.ui.Bubble.decorate($('bubble'));
 
       chrome.send('screenStateInitialize');
-
-      if (Oobe.getInstance().showingViewsLogin)
-        chrome.send('showAddUser');
     },
 
     // Dummy Oobe functions not present with stripped login UI.
diff --git a/chrome/browser/resources/chromeos/md_set_time/BUILD.gn b/chrome/browser/resources/chromeos/md_set_time/BUILD.gn
new file mode 100644
index 0000000..b4b2593b6
--- /dev/null
+++ b/chrome/browser/resources/chromeos/md_set_time/BUILD.gn
@@ -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.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+  deps = [
+    ":set_time",
+    ":set_time_browser_proxy",
+  ]
+}
+
+js_library("set_time") {
+  deps = [
+    ":set_time_browser_proxy",
+    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog",
+    "//ui/webui/resources/js:assert",
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:load_time_data",
+    "//ui/webui/resources/js:web_ui_listener_behavior",
+  ]
+}
+
+js_library("set_time_browser_proxy") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+  ]
+  externs_list = [ "$externs_path/chrome_send.js" ]
+}
diff --git a/chrome/browser/resources/chromeos/md_set_time/set_time.html b/chrome/browser/resources/chromeos/md_set_time/set_time.html
new file mode 100644
index 0000000..5abb4fd9
--- /dev/null
+++ b/chrome/browser/resources/chromeos/md_set_time/set_time.html
@@ -0,0 +1,98 @@
+<!doctype html>
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
+<head>
+<meta charset="utf-8">
+<title>$i18n{setTimeTitle}</title>
+
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
+<link rel="import" href="chrome://resources/html/load_time_data.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="set_time_browser_proxy.html">
+
+<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
+
+<script src="strings.js"></script>
+
+</head>
+
+<body>
+  <dom-module id="set-time">
+    <template>
+      <style include="cr-shared-style paper-button-style">
+        :host {
+          @apply --cr-page-host;
+        }
+
+        cr-dialog {
+          --cr-dialog-native: {
+            border-radius: 0;
+            height: 100%;
+            width: 100%;
+          }
+        }
+
+        .row {
+          margin: 0.65em 0;
+        }
+
+        input[type='date'],
+        input[type='time'] {
+          font-family: inherit;
+          font-size: 16px;
+          letter-spacing: 1px;
+          margin-bottom: 4px;
+          margin-inline-end: 8px;
+        }
+
+        input[type='date']::-webkit-clear-button,
+        input[type='time']::-webkit-clear-button {
+          display: none;
+        }
+
+        label {
+          margin-inline-end: 5px;
+        }
+
+        #timezoneSelect {
+          font-family: inherit;
+          font-size: inherit;
+        }
+      </style>
+
+      <cr-dialog id="dialog">
+        <div slot="title">$i18n{setTimeTitle}</div>
+        <div slot="body" id="dialogBody">
+          <div class="row">$i18n{prompt}</div>
+
+          <div>
+            <input id="dateInput" type="date" title="$i18n{dateLabel}"
+                on-blur="onTimeBlur_">
+            <input id="timeInput" type="time" title="$i18n{timeLabel}"
+                on-blur="onTimeBlur_">
+          </div>
+
+          <div id="timezone" class="row" hidden>
+            <label for="timezoneSelect">$i18n{timezone}</label>
+            <select id="timezoneSelect" on-change="onTimezoneChange_"></select>
+          </div>
+        </div>
+
+        <div slot="button-container">
+          <paper-button class="action-button" on-click="onDoneClick_">
+            $i18n{doneButton}
+          </paper-button>
+        </div>
+      </cr-dialog>
+    </template>
+    <script src="set_time.js"></script>
+  </dom-module>
+
+  <set-time></set-time>
+
+</body>
+</html>
diff --git a/chrome/browser/resources/chromeos/md_set_time/set_time.js b/chrome/browser/resources/chromeos/md_set_time/set_time.js
new file mode 100644
index 0000000..7f80aec5
--- /dev/null
+++ b/chrome/browser/resources/chromeos/md_set_time/set_time.js
@@ -0,0 +1,197 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'set-time' handles a dialog to check and set system time. It can also
+ * include a timezone dropdown if timezoneId is provided.
+ *
+ * 'set-time' uses the system time to populate the controls initially and
+ * update them as the system time or timezone changes, and notifies Chrome
+ * when the user changes the time or timezone.
+ */
+
+Polymer({
+  is: 'set-time',
+
+  // Remove listeners on detach.
+  behaviors: [WebUIListenerBehavior],
+
+  /**
+   * Values for reverting inputs when the user's date/time is invalid.
+   * @private {?Object}
+   */
+  prevValues_: null,
+
+  /**
+   * ID of the setTimeout() used to refresh the current time.
+   * @private {?number}
+   */
+  timeTimeoutId_: null,
+
+  /** @private {?settime.SetTimeBrowserProxy} */
+  browserProxy_: null,
+
+  /** @override */
+  created: function() {
+    this.prevValues_ = {};
+    this.browserProxy_ = settime.SetTimeBrowserProxyImpl.getInstance();
+  },
+
+  /** @override */
+  ready: function() {
+    // The build time doesn't include a timezone, so subtract 1 day to get a
+    // safe minimum date.
+    let minDate = new Date(loadTimeData.getValue('buildTime'));
+    minDate.setDate(minDate.getDate() - 1);
+
+    // Set the max date to the min date plus 20 years.
+    let maxDate = new Date(minDate);
+    maxDate.setFullYear(minDate.getFullYear() + 20);
+
+    // Make sure the ostensible date is within this range.
+    const now = new Date();
+    if (now > maxDate)
+      maxDate = now;
+    else if (now < minDate)
+      minDate = now;
+
+    // Apply the date range limit.
+    const dateInput = this.$.dateInput;
+    dateInput.setAttribute('min', this.toHtmlValues_(minDate).date);
+    dateInput.setAttribute('max', this.toHtmlValues_(maxDate).date);
+
+    this.updateTime_();
+
+    // Populate the timezone select, even if we are not going to show the
+    // element. Browser tests simulate a logged-in user, and hence don't
+    // show the timezone select, but still need to exercise it.
+    const select = this.$.timezoneSelect;
+    const timezoneList =
+        /** @type {!Array} */ (loadTimeData.getValue('timezoneList'));
+    for (const [id, name] of timezoneList) {
+      select.appendChild(new Option(name, id));
+    }
+
+    // Show the timezone select if we have a timezone ID.
+    const currentTimezoneId =
+        /** @type {string} */ (loadTimeData.getValue('currentTimezoneId'));
+    if (currentTimezoneId) {
+      this.setTimezone_(currentTimezoneId);
+      this.$.timezone.hidden = false;
+    }
+  },
+
+  /** @override */
+  attached: function() {
+    // Register listeners for updates from C++ code.
+    this.addWebUIListener('system-clock-updated', this.updateTime_.bind(this));
+    this.addWebUIListener(
+        'system-timezone-changed', this.setTimezone_.bind(this));
+
+    this.browserProxy_.sendPageReady();
+
+    /** @type {!CrDialogElement} */ (this.$.dialog).showModal();
+  },
+
+  /**
+   * Sets the current timezone.
+   * @param {string} timezoneId The timezone ID to select.
+   * @private
+   */
+  setTimezone_: function(timezoneId) {
+    this.$.timezoneSelect.value = timezoneId;
+    this.updateTime_();
+  },
+
+  /**
+   * Updates the date/time controls to the current local time.
+   * Called initially, then called again once a minute.
+   * @private
+   */
+  updateTime_: function() {
+    const now = new Date();
+
+    // Only update time controls if neither is focused.
+    if (document.activeElement.id != 'dateInput' &&
+        document.activeElement.id != 'timeInput') {
+      const htmlValues = this.toHtmlValues_(now);
+      this.prevValues_.date = this.$.dateInput.value = htmlValues.date;
+      this.prevValues_.time = this.$.timeInput.value = htmlValues.time;
+    }
+
+    if (this.timeTimeoutId_) {
+      window.clearTimeout(this.timeTimeoutId_);
+    }
+
+    // Start timer to update these inputs every minute.
+    const secondsRemaining = 60 - now.getSeconds();
+    this.timeTimeoutId_ =
+        window.setTimeout(this.updateTime_.bind(this), secondsRemaining * 1000);
+  },
+
+  /**
+   * Sets the system time from the UI.
+   * @private
+   */
+  applyTime_: function() {
+    const date = this.$.dateInput.valueAsDate;
+    date.setMilliseconds(
+        date.getMilliseconds() + this.$.timeInput.valueAsNumber);
+
+    // Add timezone offset to get real time.
+    date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
+
+    const seconds = Math.floor(date / 1000);
+    this.browserProxy_.setTimeInSeconds(seconds);
+  },
+
+  /**
+   * Called when focus is lost on date/time controls.
+   * @param {!Event} e The blur event.
+   * @private
+   */
+  onTimeBlur_: function(e) {
+    if (e.target.validity.valid && e.target.value) {
+      // Make this the new fallback time in case of future invalid input.
+      this.prevValues_[e.target.id] = e.target.value;
+      this.applyTime_();
+    } else {
+      // Restore previous value.
+      e.target.value = this.prevValues_[e.target.id];
+    }
+  },
+
+  /**
+   * @param {!Event} e The change event.
+   * @private
+   */
+  onTimezoneChange_: function(e) {
+    this.browserProxy_.setTimezone(e.currentTarget.value);
+  },
+
+  /** @private */
+  onDoneClick_: function() {
+    this.browserProxy_.dialogClose();
+  },
+
+  /**
+   * Builds date and time strings suitable for the values of HTML date and
+   * time elements.
+   * @param {!Date} date The date object to represent.
+   * @return {!{date: string, time: string}} Date is an RFC 3339 formatted date
+   *     and time is an HH:MM formatted time.
+   * @private
+   */
+  toHtmlValues_: function(date) {
+    // Get the current time and subtract the timezone offset, so the
+    // JSON string is in local time.
+    const localDate = new Date(date);
+    localDate.setMinutes(date.getMinutes() - date.getTimezoneOffset());
+    return {
+      date: localDate.toISOString().slice(0, 10),
+      time: localDate.toISOString().slice(11, 16)
+    };
+  },
+});
diff --git a/chrome/browser/resources/chromeos/md_set_time/set_time_browser_proxy.html b/chrome/browser/resources/chromeos/md_set_time/set_time_browser_proxy.html
new file mode 100644
index 0000000..8cf05d3
--- /dev/null
+++ b/chrome/browser/resources/chromeos/md_set_time/set_time_browser_proxy.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<script src="set_time_browser_proxy.js"></script>
diff --git a/chrome/browser/resources/chromeos/md_set_time/set_time_browser_proxy.js b/chrome/browser/resources/chromeos/md_set_time/set_time_browser_proxy.js
new file mode 100644
index 0000000..3ec1efa
--- /dev/null
+++ b/chrome/browser/resources/chromeos/md_set_time/set_time_browser_proxy.js
@@ -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.
+
+/** @fileoverview A helper object used by the "Set Time" dialog. */
+cr.define('settime', function() {
+  /** @interface */
+  class SetTimeBrowserProxy {
+    /** Notifies C++ code that it's safe to call JS functions. */
+    sendPageReady() {}
+
+    /** @param {number} timeInSeconds */
+    setTimeInSeconds(timeInSeconds) {}
+
+    /** @param {string} timezone */
+    setTimezone(timezone) {}
+
+    /** Closes the dialog. */
+    dialogClose() {}
+  }
+
+  /** @implements {settime.SetTimeBrowserProxy} */
+  class SetTimeBrowserProxyImpl {
+    /** @override */
+    sendPageReady() {
+      chrome.send('setTimePageReady');
+    }
+
+    /** @override */
+    setTimeInSeconds(timeInSeconds) {
+      chrome.send('setTimeInSeconds', [timeInSeconds]);
+    }
+
+    /** @override */
+    setTimezone(timezone) {
+      chrome.send('setTimezone', [timezone]);
+    }
+
+    /** @override */
+    dialogClose() {
+      chrome.send('dialogClose');
+    }
+  }
+
+  cr.addSingletonGetter(SetTimeBrowserProxyImpl);
+
+  return {
+    SetTimeBrowserProxy: SetTimeBrowserProxy,
+    SetTimeBrowserProxyImpl: SetTimeBrowserProxyImpl,
+  };
+});
diff --git a/chrome/browser/resources/chromeos/set_time/set_time.html b/chrome/browser/resources/chromeos/set_time/set_time.html
index 0501352..ad36057c 100644
--- a/chrome/browser/resources/chromeos/set_time/set_time.html
+++ b/chrome/browser/resources/chromeos/set_time/set_time.html
@@ -21,6 +21,7 @@
   <div id="timezone" class="row" hidden>
     <label id="timezone-label" for="timezone-select" i18n-content="timezone">
     </label>
+    <!-- NOTE: i18n-options can be deleted when this reference is removed. -->
     <select id="timezone-select" i18n-options="timezoneList"></select>
   </div>
 
diff --git a/chrome/browser/resources/conflicts/about_conflicts.html b/chrome/browser/resources/conflicts/about_conflicts.html
index 4ee426b..c68765ff 100644
--- a/chrome/browser/resources/conflicts/about_conflicts.html
+++ b/chrome/browser/resources/conflicts/about_conflicts.html
@@ -258,12 +258,9 @@
 </div>
 <script src="chrome://resources/js/cr.js"></script>
 <script src="chrome://resources/js/jstemplate_compiled.js"></script>
-<script src="chrome://resources/js/load_time_data.js"></script>
 <script src="chrome://resources/js/promise_resolver.js"></script>
 <script src="chrome://resources/js/util.js"></script>
 
-<script src="chrome://conflicts/strings.js"></script>
-
 <script src="chrome://conflicts/conflicts.js"></script>
 </body>
 </html>
diff --git a/chrome/browser/resources/download_internals/BUILD.gn b/chrome/browser/resources/download_internals/BUILD.gn
index 53a4def..e1314cf 100644
--- a/chrome/browser/resources/download_internals/BUILD.gn
+++ b/chrome/browser/resources/download_internals/BUILD.gn
@@ -17,7 +17,6 @@
     ":download_internals_browser_proxy",
     "//ui/webui/resources/js:assert",
     "//ui/webui/resources/js:cr",
-    "//ui/webui/resources/js:load_time_data",
     "//ui/webui/resources/js:util",
   ]
 }
diff --git a/chrome/browser/resources/download_internals/download_internals.html b/chrome/browser/resources/download_internals/download_internals.html
index 1c71deee..62b2d8c 100644
--- a/chrome/browser/resources/download_internals/download_internals.html
+++ b/chrome/browser/resources/download_internals/download_internals.html
@@ -11,10 +11,8 @@
     <link rel="import" href="chrome://resources/html/cr.html">
     <link rel="import" href="chrome://resources/html/cr/ui/list_item.html">
     <link rel="import" href="chrome://resources/html/cr/ui/list.html">
-    <link rel="import" href="chrome://resources/html/load_time_data.html">
     <link rel="import" href="chrome://resources/html/util.html">
 
-    <script src="strings.js"></script>
     <script src="download_internals_browser_proxy.js"></script>
     <script src="download_internals.js"></script>
     <script src="download_internals_visuals.js"></script>
diff --git a/chrome/browser/resources/md_extensions/activity_log/activity_log_stream.js b/chrome/browser/resources/md_extensions/activity_log/activity_log_stream.js
index fcc8da9e..7d570e9 100644
--- a/chrome/browser/resources/md_extensions/activity_log/activity_log_stream.js
+++ b/chrome/browser/resources/md_extensions/activity_log/activity_log_stream.js
@@ -46,6 +46,7 @@
                                  pageUrl: activity.pageUrl,
                                  timestamp,
                                  webRequestInfo,
+                                 expanded: false,
                                }));
   }
 
diff --git a/chrome/browser/resources/md_extensions/activity_log/activity_log_stream_item.html b/chrome/browser/resources/md_extensions/activity_log/activity_log_stream_item.html
index 0850e5d..68d921c 100644
--- a/chrome/browser/resources/md_extensions/activity_log/activity_log_stream_item.html
+++ b/chrome/browser/resources/md_extensions/activity_log/activity_log_stream_item.html
@@ -108,11 +108,11 @@
         <span id="activity-name" title="[[data.name]]">[[data.name]]</span>
         <span id="activity-time">[[getFormattedTime_(data.timeStamp)]]</span>
       </div>
-      <cr-expand-button expanded="{{isExpanded_}}"
+      <cr-expand-button expanded="{{data.expanded}}"
           hidden$="[[!isExpandable_]]">
       </cr-expand-button>
     </div>
-    <iron-collapse opened="[[isExpanded_]]">
+    <iron-collapse opened="[[data.expanded]]">
       <div id="expanded-data" hidden$="[[!isExpandable_]]">
         <a id="page-url-link" href="[[data.pageUrl]]"
             hidden$="[[!hasPageUrl_(data.pageUrl)]]"
diff --git a/chrome/browser/resources/md_extensions/activity_log/activity_log_stream_item.js b/chrome/browser/resources/md_extensions/activity_log/activity_log_stream_item.js
index 070500e9..0d55506 100644
--- a/chrome/browser/resources/md_extensions/activity_log/activity_log_stream_item.js
+++ b/chrome/browser/resources/md_extensions/activity_log/activity_log_stream_item.js
@@ -13,7 +13,8 @@
    *   pageUrl: string,
    *   argUrl: string,
    *   args: string,
-   *   webRequestInfo: (string|undefined)
+   *   webRequestInfo: (string|undefined),
+   *   expanded: boolean
    * }}
    */
   let StreamItem;
@@ -68,12 +69,6 @@
         type: Boolean,
         computed: 'computeIsExpandable_(data)',
       },
-
-      /** @private */
-      isExpanded_: {
-        type: Boolean,
-        value: false,
-      },
     },
 
     /**
@@ -152,7 +147,7 @@
     /** @private */
     onExpandClick_: function() {
       if (this.isExpandable_) {
-        this.isExpanded_ = !this.isExpanded_;
+        this.set('data.expanded', !this.data.expanded);
       }
     },
   });
diff --git a/chrome/browser/resources/net_internals/index.html b/chrome/browser/resources/net_internals/index.html
index 417ec37..56f50557 100644
--- a/chrome/browser/resources/net_internals/index.html
+++ b/chrome/browser/resources/net_internals/index.html
@@ -15,9 +15,7 @@
     <link rel="stylesheet" href="chromeos_view.css">
     <script src="chrome://resources/js/util.js"></script>
     <script src="chrome://resources/js/cr.js"></script>
-    <script src="chrome://resources/js/load_time_data.js"></script>
     <script src="chrome://net-internals/index.js"></script>
-    <script src="chrome://net-internals/strings.js"></script>
   </head>
   <body id=events-view-drop-target>
     <div id="tab-list">
diff --git a/chrome/browser/resources/print_preview/new/app.js b/chrome/browser/resources/print_preview/new/app.js
index d6f4d98b..3b1aeab 100644
--- a/chrome/browser/resources/print_preview/new/app.js
+++ b/chrome/browser/resources/print_preview/new/app.js
@@ -587,7 +587,7 @@
     // Destination settings is always available. See if the total number of
     // available sections exceeds the maximum number to show.
     return [
-      'pages', 'copies', 'layout', 'color', 'mediaSize', 'margins', 'color',
+      'pages', 'copies', 'layout', 'color', 'mediaSize', 'margins', 'dpi',
       'pagesPerSheet', 'scaling', 'otherOptions', 'vendorItems'
     ].reduce((count, setting) => {
       return this.getSetting(setting).available ? count + 1 : count;
diff --git a/chrome/browser/resources/print_preview/new/preview_area.js b/chrome/browser/resources/print_preview/new/preview_area.js
index a975619..bc8cfe4 100644
--- a/chrome/browser/resources/print_preview/new/preview_area.js
+++ b/chrome/browser/resources/print_preview/new/preview_area.js
@@ -189,13 +189,44 @@
     marginControlContainer.setInvisible(true);
   },
 
+  /**
+   * @return {boolean} Whether margin settings are valid for the print ticket.
+   * @private
+   */
+  marginsValid_: function() {
+    const type = this.getSettingValue('margins');
+    if (!Object.values(print_preview.ticket_items.MarginsTypeValue)
+             .includes(type)) {
+      // Unrecognized margins type.
+      return false;
+    }
+
+    if (type !== print_preview.ticket_items.MarginsTypeValue.CUSTOM) {
+      return true;
+    }
+
+    const customMargins = this.getSettingValue('customMargins');
+    return customMargins.marginTop !== undefined &&
+        customMargins.marginLeft !== undefined &&
+        customMargins.marginBottom !== undefined &&
+        customMargins.marginRight !== undefined;
+  },
+
   /** @private */
   onSettingsChanged_: function() {
-    if (this.state == print_preview_new.State.READY) {
-      this.startPreview_();
+    if (!this.marginsValid_()) {
+      // Log so that we can try to debug how this occurs. See
+      // https://crbug.com/942211
+      console.warn('Trying to request a preview with invalid margins');
       return;
     }
-    this.requestPreviewWhenReady_ = true;
+
+    if (this.state !== print_preview_new.State.READY) {
+      this.requestPreviewWhenReady_ = true;
+      return;
+    }
+
+    this.startPreview_();
   },
 
   /** @private */
@@ -530,30 +561,38 @@
 
   /** @private */
   onMarginsChanged_: function() {
-    if (this.getSettingValue('margins') !=
-        print_preview.ticket_items.MarginsTypeValue.CUSTOM) {
+    const marginsValid = this.marginsValid_();
+    if (marginsValid &&
+        this.getSettingValue('margins') !==
+            print_preview.ticket_items.MarginsTypeValue.CUSTOM) {
       this.onSettingsChanged_();
-    } else {
-      const customMargins =
-          /** @type {!print_preview.MarginsSetting} */ (
-              this.getSettingValue('customMargins'));
-
-      for (const side of Object.values(
-               print_preview.ticket_items.CustomMarginsOrientation)) {
-        const key = print_preview_new.MARGIN_KEY_MAP.get(side);
-        // If custom margins are undefined, return and wait for them to be set.
-        if (customMargins[key] === undefined || !this.margins) {
-          return;
-        }
-
-        // Start a preview request if the margins actually changed.
-        if (this.margins.get(side) != customMargins[key]) {
-          this.onSettingsChanged_();
-          break;
-        }
-      }
-      this.lastCustomMargins_ = customMargins;
+      return;
     }
+
+    if (!this.margins) {
+      return;
+    }
+
+    if (!marginsValid) {
+      // Log so that we can try to debug how this occurs. See
+      // https://crbug.com/942211
+      console.warn('Margins changed to custom with invalid values');
+      return;
+    }
+
+    const customMargins =
+        /** @type {!print_preview.MarginsSetting} */ (
+            this.getSettingValue('customMargins'));
+    const marginSides =
+        Object.values(print_preview.ticket_items.CustomMarginsOrientation);
+    const customMarginsChanged = marginSides.some(side => {
+      return this.margins.get(side) !==
+          customMargins[print_preview_new.MARGIN_KEY_MAP.get(side)];
+    });
+    if (customMarginsChanged) {
+      this.onSettingsChanged_();
+    }
+    this.lastCustomMargins_ = customMargins;
   },
 
   /** @private */
diff --git a/chrome/browser/resources/quota_internals/main.html b/chrome/browser/resources/quota_internals/main.html
index 668327151..a1a1b11 100644
--- a/chrome/browser/resources/quota_internals/main.html
+++ b/chrome/browser/resources/quota_internals/main.html
@@ -13,7 +13,6 @@
 <script src="chrome://resources/js/util.js"></script>
 <script src="chrome://resources/js/cr.js"></script>
 <script src="chrome://resources/js/cr/event_target.js"></script>
-<script src="chrome://resources/js/load_time_data.js"></script>
 
 <link rel="stylesheet" href="chrome://resources/css/tabs.css">
 <link rel="stylesheet" href="chrome://resources/css/tree.css">
@@ -24,7 +23,6 @@
 
 <script src="chrome://quota-internals/message_dispatcher.js"></script>
 <script src="chrome://quota-internals/event_handler.js"></script>
-<script src="chrome://quota-internals/strings.js"></script>
 
 <body>
 
diff --git a/chrome/browser/resources/settings/crostini_page/BUILD.gn b/chrome/browser/resources/settings/crostini_page/BUILD.gn
index 1ab1fba..bc66368 100644
--- a/chrome/browser/resources/settings/crostini_page/BUILD.gn
+++ b/chrome/browser/resources/settings/crostini_page/BUILD.gn
@@ -17,7 +17,6 @@
 js_library("crostini_browser_proxy") {
   deps = [
     "//ui/webui/resources/js:cr",
-    "//ui/webui/resources/js:web_ui_listener_behavior",
   ]
 }
 
@@ -43,7 +42,7 @@
   deps = [
     ":crostini_browser_proxy",
     "..:route",
-    "../prefs:prefs_behavior",
+    "//ui/webui/resources/js:web_ui_listener_behavior",
   ]
 }
 
diff --git a/chrome/browser/resources/settings/crostini_page/crostini_browser_proxy.js b/chrome/browser/resources/settings/crostini_page/crostini_browser_proxy.js
index fd3df48..57f35c3 100644
--- a/chrome/browser/resources/settings/crostini_page/crostini_browser_proxy.js
+++ b/chrome/browser/resources/settings/crostini_page/crostini_browser_proxy.js
@@ -9,6 +9,13 @@
 let CrostiniSharedPath;
 
 /**
+ * @typedef {{label: string,
+ *            guid: string,
+ *            shared: boolean}}
+ */
+let CrostiniSharedUsbDevice;
+
+/**
  * @fileoverview A helper object used by the "Linux Apps" (Crostini) section
  * to install and uninstall Crostini.
  */
@@ -27,6 +34,17 @@
      */
     getCrostiniSharedPathsDisplayText(paths) {}
 
+    /**
+     * @return {!Promise<!Array<CrostiniSharedUsbDevice>>}
+     */
+    getCrostiniSharedUsbDevices() {}
+
+    /**
+     * @param {string} guid Unique device identifier.
+     * @param {boolean} shared Whether device is currently shared with Crostini.
+     */
+    setCrostiniUsbDeviceShared(guid, shared) {}
+
     /** @param {string} path Path to stop sharing. */
     removeCrostiniSharedPath(path) {}
 
@@ -59,6 +77,16 @@
     }
 
     /** @override */
+    getCrostiniSharedUsbDevices() {
+      return cr.sendWithPromise('getCrostiniSharedUsbDevices');
+    }
+
+    /** @override */
+    setCrostiniUsbDeviceShared(guid, shared) {
+      return chrome.send('setCrostiniUsbDeviceShared', [guid, shared]);
+    }
+
+    /** @override */
     removeCrostiniSharedPath(path) {
       chrome.send('removeCrostiniSharedPath', [path]);
     }
diff --git a/chrome/browser/resources/settings/crostini_page/crostini_shared_usb_devices.html b/chrome/browser/resources/settings/crostini_page/crostini_shared_usb_devices.html
index 48dcb3a0..20f1283 100644
--- a/chrome/browser/resources/settings/crostini_page/crostini_shared_usb_devices.html
+++ b/chrome/browser/resources/settings/crostini_page/crostini_shared_usb_devices.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="../prefs/prefs_behavior.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
 <link rel="import" href="../settings_shared_css.html">
 
@@ -16,20 +16,27 @@
       }
     </style>
     <div class="settings-box first">
-      <div>$i18n{crostiniSharedUsbDevicesDescription}</div>
-    </div>
-    <div class="settings-box continuation">
-      <h2 class="start">$i18n{crostiniSharedUsbDevicesListHeading}</h2>
-    </div>
-    <div class="list-frame vertical-list">
-      <template is="dom-repeat"
-        items="{{prefs.crostini.shared_usb_devices.value}}" as="device">
-        <div class="toggle-container settings-box">
-          <div class="label">[[device.name]]</div>
-          <cr-toggle class="toggle" checked="{{device.shared}}"></cr-toggle>
+      <div class="settings-box-text">
+        $i18n{crostiniSharedUsbDevicesDescription}
+        <div class="secondary" id="secondaryText">
+          $i18n{crostiniSharedUsbDevicesExtraDescription}
         </div>
-      </template>
+      </div>
     </div>
+    <div class="settings-box secondary continuation"
+        hidden="[[sharedUsbDevices_.length]]" >
+       $i18n{crostiniSharedUsbDevicesListEmptyMessage}
+    </div>
+    <div class="list-frame vertical-list"
+        hidden="[[!sharedUsbDevices_.length]]">
+      <template is="dom-repeat" items="[[sharedUsbDevices_]]">
+        <div class="toggle-container settings-box">
+          <div class="label">[[item.label]]</div>
+          <cr-toggle class="toggle" checked="[[item.shared]]"
+            on-change="onDeviceSharedChange_"></cr-toggle>
+        </div>
+      </div>
+    </template>
   </template>
   <script src="crostini_shared_usb_devices.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/crostini_page/crostini_shared_usb_devices.js b/chrome/browser/resources/settings/crostini_page/crostini_shared_usb_devices.js
index 02951ec..8d4db11 100644
--- a/chrome/browser/resources/settings/crostini_page/crostini_shared_usb_devices.js
+++ b/chrome/browser/resources/settings/crostini_page/crostini_shared_usb_devices.js
@@ -11,13 +11,41 @@
 Polymer({
   is: 'settings-crostini-shared-usb-devices',
 
-  behaviors: [PrefsBehavior],
+  behaviors: [WebUIListenerBehavior],
 
   properties: {
-    /** Preferences state. */
-    prefs: {
-      type: Object,
-      notify: true,
-    },
+    /**
+     * The USB Devices available for connection to a VM.
+     * @private {Array<!CrostiniSharedUsbDevice>}
+     */
+    sharedUsbDevices_: Array,
+  },
+
+  /** @override */
+  attached: function() {
+    this.addWebUIListener(
+        'crostini-shared-usb-devices-changed',
+        this.onCrostiniSharedUsbDevicesChanged_.bind(this));
+    settings.CrostiniBrowserProxyImpl.getInstance()
+        .getCrostiniSharedUsbDevices()
+        .then(this.onCrostiniSharedUsbDevicesChanged_.bind(this));
+  },
+
+  /**
+   * @param {!Array<CrostiniSharedUsbDevice>} devices
+   * @private
+   */
+  onCrostiniSharedUsbDevicesChanged_: function(devices) {
+    this.sharedUsbDevices_ = devices;
+  },
+
+  /**
+   * @param {!CustomEvent<!CrostiniSharedUsbDevice>} event
+   * @private
+   */
+  onDeviceSharedChange_: function(event) {
+    const deviceInfo = event.model.item;
+    settings.CrostiniBrowserProxyImpl.getInstance().setCrostiniUsbDeviceShared(
+        deviceInfo.guid, event.target.checked);
   },
 });
diff --git a/chrome/browser/resources/settings/kiosk_next_shell_page/BUILD.gn b/chrome/browser/resources/settings/kiosk_next_shell_page/BUILD.gn
index b382f65..bde7e39 100644
--- a/chrome/browser/resources/settings/kiosk_next_shell_page/BUILD.gn
+++ b/chrome/browser/resources/settings/kiosk_next_shell_page/BUILD.gn
@@ -14,7 +14,7 @@
 js_library("kiosk_next_shell_page") {
   deps = [
     "../prefs:prefs_behavior",
-    "//ui/webui/resources/js:i18n_behavior",
+    "//ui/webui/resources/js:load_time_data",
   ]
 }
 
@@ -22,6 +22,6 @@
   deps = [
     "..:lifetime_browser_proxy",
     "../prefs:prefs_behavior",
-    "//ui/webui/resources/js:i18n_behavior",
+    "//ui/webui/resources/js:load_time_data",
   ]
 }
diff --git a/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.html b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.html
index 9038ff8..1d1d0624 100644
--- a/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.html
+++ b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.html
@@ -1,15 +1,14 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="../lifetime_browser_proxy.html">
 <link rel="import" href="../prefs/prefs_behavior.html">
 <link rel="import" href="../settings_shared_css.html">
 
 <dom-module id="settings-kiosk-next-shell-confirmation-dialog">
   <template>
-    <style include="settings-shared">
-    </style>
+    <style include="settings-shared"></style>
     <cr-dialog id="dialog" close-text="$i18n{close}" ignore-enter-key>
       <div slot="title">
         [[getTitleText_(prefs.ash.kiosk_next_shell.enabled.value)]]
@@ -19,13 +18,13 @@
       </div>
       <div slot="button-container">
         <paper-button class="cancel-button"
-            on-click="onCancelTap_"
+            on-click="onCancelClick_"
             id="cancel">
           $i18n{cancel}
         </paper-button>
         <paper-button class="action-button"
             id="confirm"
-            on-click="onConfirmTap_">
+            on-click="onConfirmClick_">
           [[getConfirmationText_(prefs.ash.kiosk_next_shell.enabled.value)]]
         </paper-button>
       </div>
diff --git a/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.js b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.js
index 8d60ea4a..5c82394a 100644
--- a/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.js
+++ b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.js
@@ -12,7 +12,7 @@
 Polymer({
   is: 'settings-kiosk-next-shell-confirmation-dialog',
 
-  behaviors: [I18nBehavior, PrefsBehavior],
+  behaviors: [PrefsBehavior],
 
   properties: {
     /** Preferences state. */
@@ -31,8 +31,8 @@
    * @param {!Event} event
    * @private
    */
-  onCancelTap_: function(event) {
-    this.$.dialog.close();
+  onCancelClick_: function(event) {
+    this.$.dialog.cancel();
     event.stopPropagation();
   },
 
@@ -40,39 +40,44 @@
    * @param {!Event} event
    * @private
    */
-  onConfirmTap_: function(event) {
+  onConfirmClick_: function(event) {
     const prefPath = 'ash.kiosk_next_shell.enabled';
     this.setPrefValue(prefPath, !this.getPref(prefPath).value);
     settings.LifetimeBrowserProxyImpl.getInstance().signOutAndRestart();
+    this.$.dialog.close();
     event.stopPropagation();
   },
 
   /**
-   * @private
+   * @param {boolean} kioskNextShellEnabled
    * @return {string}
+   * @private
    */
   getTitleText_: function(kioskNextShellEnabled) {
-    return kioskNextShellEnabled ?
-        this.i18n('kioskNextShellEnabledDialogTitle') :
-        this.i18n('kioskNextShellDisabledDialogTitle');
+    return loadTimeData.getString(
+        kioskNextShellEnabled ? 'kioskNextShellEnabledDialogTitle' :
+                                'kioskNextShellDisabledDialogTitle');
   },
 
   /**
-   * @private
+   * @param {boolean} kioskNextShellEnabled
    * @return {string}
+   * @private
    */
   getBodyText_: function(kioskNextShellEnabled) {
-    return kioskNextShellEnabled ?
-        this.i18n('kioskNextShellEnabledDialogBody') :
-        this.i18n('kioskNextShellDisabledDialogBody');
+    return loadTimeData.getString(
+        kioskNextShellEnabled ? 'kioskNextShellEnabledDialogBody' :
+                                'kioskNextShellDisabledDialogBody');
   },
 
   /**
-   * @private
+   * @param {boolean} kioskNextShellEnabled
    * @return {string}
+   * @private
    */
   getConfirmationText_: function(kioskNextShellEnabled) {
-    return kioskNextShellEnabled ? this.i18n('kioskNextShellTurnOff') :
-                                   this.i18n('kioskNextShellTurnOn');
+    return loadTimeData.getString(
+        kioskNextShellEnabled ? 'kioskNextShellTurnOff' :
+                                'kioskNextShellTurnOn');
   },
 });
diff --git a/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.html b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.html
index 1ee63eb3..db08f3a 100644
--- a/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.html
+++ b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.html
@@ -1,6 +1,5 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="../i18n_setup.html">
 <link rel="import" href="../prefs/prefs_behavior.html">
@@ -12,6 +11,7 @@
     <style include="settings-shared"></style>
     <template is="dom-if" if="[[showConfirmationDialog_]]" restamp>
       <settings-kiosk-next-shell-confirmation-dialog
+          id="dialog"
           on-close="onConfirmationDialogClose_"
           prefs="{{prefs}}">
       </settings-kiosk-next-shell-confirmation-dialog>
@@ -24,8 +24,8 @@
         </div>
       </div>
       <div class="separator"></div>
-      <paper-button id="enable"
-          on-click="onToggleButtonPressed_"
+      <paper-button
+          on-click="onToggleButtonClick_"
           aria-label="$i18n{kioskNextShellPageTitle}"
           aria-describedby="secondaryText">
         [[getButtonLabel_(prefs.ash.kiosk_next_shell.enabled.value)]]
diff --git a/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.js b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.js
index 4b685acd6..4485674 100644
--- a/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.js
+++ b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.js
@@ -10,7 +10,7 @@
 Polymer({
   is: 'settings-kiosk-next-shell-page',
 
-  behaviors: [I18nBehavior, PrefsBehavior],
+  behaviors: [PrefsBehavior],
 
   properties: {
     /** Preferences state. */
@@ -19,6 +19,7 @@
       notify: true,
     },
 
+    /** @private */
     showConfirmationDialog_: Boolean,
   },
 
@@ -26,7 +27,7 @@
    * @private
    * @param {!Event} event
    */
-  onToggleButtonPressed_: function(event) {
+  onToggleButtonClick_: function(event) {
     this.showConfirmationDialog_ = true;
     event.stopPropagation();
   },
@@ -46,9 +47,9 @@
    * @return {string}
    */
   getSubtextLabel_: function(kioskNextShellEnabled) {
-    return kioskNextShellEnabled
-        ? this.i18n('kioskNextShellPageSubtextDisable')
-        : this.i18n('kioskNextShellPageSubtextEnable');
+    return loadTimeData.getString(
+      kioskNextShellEnabled ? 'kioskNextShellPageSubtextDisable' :
+                              'kioskNextShellPageSubtextEnable');
   },
 
   /**
@@ -57,8 +58,8 @@
    * @return {string}
    */
   getButtonLabel_: function(kioskNextShellEnabled) {
-    return kioskNextShellEnabled
-        ? this.i18n('kioskNextShellTurnOff')
-        : this.i18n('kioskNextShellTurnOn');
+    return loadTimeData.getString(
+      kioskNextShellEnabled ? 'kioskNextShellTurnOff' :
+                              'kioskNextShellTurnOn');
   }
 });
diff --git a/chrome/browser/resources/supervised_user_internals/supervised_user_internals.html b/chrome/browser/resources/supervised_user_internals/supervised_user_internals.html
index 3ae9ad2..809060b1 100644
--- a/chrome/browser/resources/supervised_user_internals/supervised_user_internals.html
+++ b/chrome/browser/resources/supervised_user_internals/supervised_user_internals.html
@@ -12,7 +12,6 @@
 <link rel="stylesheet" href="chrome://resources/css/list.css">
 <link rel="stylesheet" href="supervised_user_internals.css">
 <script src="chrome://resources/js/cr.js"></script>
-<script src="chrome://resources/js/load_time_data.js"></script>
 <script src="chrome://resources/js/util.js"></script>
 </head>
 
diff --git a/chrome/browser/resources/sync_file_system_internals/main.html b/chrome/browser/resources/sync_file_system_internals/main.html
index c058fbe..b0fe206 100644
--- a/chrome/browser/resources/sync_file_system_internals/main.html
+++ b/chrome/browser/resources/sync_file_system_internals/main.html
@@ -15,11 +15,9 @@
 <script src="chrome://resources/js/cr/event_target.js"></script>
 
 <link rel="stylesheet" href="chrome://resources/css/tabs.css">
-<script src="chrome://resources/js/load_time_data.js"></script>
 <script src="chrome://resources/js/cr/ui.js"></script>
 <script src="chrome://resources/js/cr/ui/tabs.js"></script>
 <script src="chrome://resources/js/cr/ui/focus_outline_manager.js"></script>
-<script src="chrome://syncfs-internals/strings.js"></script>
 <script src="chrome://syncfs-internals/utils.js"></script>
 
 <body>
diff --git a/chrome/browser/send_tab_to_self/desktop_notification_handler.cc b/chrome/browser/send_tab_to_self/desktop_notification_handler.cc
index aa4d17c..01131039 100644
--- a/chrome/browser/send_tab_to_self/desktop_notification_handler.cc
+++ b/chrome/browser/send_tab_to_self/desktop_notification_handler.cc
@@ -8,7 +8,6 @@
 #include <utility>
 
 #include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/notifications/notification_display_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -34,23 +33,19 @@
 
 void DesktopNotificationHandler::DisplayNewEntry(
     const SendTabToSelfEntry* entry) {
-  const base::string16& device_info =
-      l10n_util::GetStringUTF16(
-          IDS_MESSAGE_NOTIFICATION_SEND_TAB_TO_SELF_DEVICE_INFO_PREFIX) +
-      base::UTF8ToUTF16(" " + entry->GetDeviceName());
-  const gfx::Image notification_icon = gfx::Image();
+  const base::string16 device_info = l10n_util::GetStringFUTF16(
+      IDS_MESSAGE_NOTIFICATION_SEND_TAB_TO_SELF_DEVICE_INFO,
+      base::UTF8ToUTF16(entry->GetDeviceName()));
   const GURL& url = entry->GetURL();
   message_center::RichNotificationData optional_fields;
   // Set the notification to be persistent
   optional_fields.never_timeout = true;
-
   // Declare a notification
   message_center::Notification notification(
       message_center::NOTIFICATION_TYPE_SIMPLE, entry->GetGUID(),
-      base::UTF8ToUTF16(entry->GetTitle()), device_info, notification_icon,
+      base::UTF8ToUTF16(entry->GetTitle()), device_info, gfx::Image(),
       base::UTF8ToUTF16(url.host()), url, message_center::NotifierId(url),
       optional_fields, nullptr);
-
   NotificationDisplayServiceFactory::GetForProfile(profile_)->Display(
       NotificationHandler::Type::SEND_TAB_TO_SELF, notification);
 }
diff --git a/chrome/browser/send_tab_to_self/desktop_notification_handler.h b/chrome/browser/send_tab_to_self/desktop_notification_handler.h
index cb4cd86..62524ca2 100644
--- a/chrome/browser/send_tab_to_self/desktop_notification_handler.h
+++ b/chrome/browser/send_tab_to_self/desktop_notification_handler.h
@@ -36,7 +36,6 @@
                const std::string& notification_id,
                bool by_user,
                base::OnceClosure completed_closure) override;
-
   void OnClick(Profile* profile,
                const GURL& origin,
                const std::string& notification_id,
@@ -45,7 +44,7 @@
                base::OnceClosure completed_closure) override;
 
  protected:
-  Profile* profile_;
+  Profile* const profile_;
   DISALLOW_COPY_AND_ASSIGN(DesktopNotificationHandler);
 };
 
diff --git a/chrome/browser/supervised_user/kids_management_url_checker_client.cc b/chrome/browser/supervised_user/kids_management_url_checker_client.cc
index 184649e4..3a8f1ac 100644
--- a/chrome/browser/supervised_user/kids_management_url_checker_client.cc
+++ b/chrome/browser/supervised_user/kids_management_url_checker_client.cc
@@ -175,7 +175,9 @@
           trigger:
             "If the parent enabled this feature for the child account, this is "
             "sent for every navigation."
-          data: "URL(s) to be checked."
+          data:
+            "The request is authenticated with an OAuth2 access token "
+            "identifying the Google account. The URL to be checked."
           destination: GOOGLE_OWNED_SERVICE
         }
         policy {
diff --git a/chrome/browser/themes/theme_properties.cc b/chrome/browser/themes/theme_properties.cc
index 12214ba..77e8566 100644
--- a/chrome/browser/themes/theme_properties.cc
+++ b/chrome/browser/themes/theme_properties.cc
@@ -54,6 +54,8 @@
     case ThemeProperties::COLOR_TAB_CLOSE_BUTTON_ACTIVE:
     case ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON:
       return gfx::kGoogleGrey100;
+    case ThemeProperties::COLOR_NTP_TEXT:
+      return gfx::kGoogleGrey200;
     case ThemeProperties::COLOR_BACKGROUND_TAB_TEXT:
     case ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INACTIVE:
     case ThemeProperties::COLOR_TAB_CLOSE_BUTTON_INACTIVE:
diff --git a/chrome/browser/ui/cocoa/applescript/apple_event_util.mm b/chrome/browser/ui/cocoa/applescript/apple_event_util.mm
index 16d6856..25a59338 100644
--- a/chrome/browser/ui/cocoa/applescript/apple_event_util.mm
+++ b/chrome/browser/ui/cocoa/applescript/apple_event_util.mm
@@ -96,6 +96,16 @@
       }
       break;
     }
+
+    // TODO(crbug.com/859477): Remove after root cause is found.
+    case base::Value::Type::DEAD:
+      CHECK(false);
+      break;
+
+    // TODO(crbug.com/859477): Remove after root cause is found.
+    default:
+      CHECK(false);
+      break;
   }
 
   return descriptor;
diff --git a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
index 18e6b1f..334b9964 100644
--- a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
+++ b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
@@ -101,6 +101,7 @@
           true /* is_before_first_responder */, &was_executed);
       if (was_executed)
         return ui::PerformKeyEquivalentResult::kHandled;
+      bridge->SaveKeyEventForRedispatch(event);
     }
   }
 
diff --git a/chrome/browser/ui/views/chrome_views_delegate.h b/chrome/browser/ui/views/chrome_views_delegate.h
index b33def5..b97f085 100644
--- a/chrome/browser/ui/views/chrome_views_delegate.h
+++ b/chrome/browser/ui/views/chrome_views_delegate.h
@@ -43,7 +43,7 @@
   HICON GetDefaultWindowIcon() const override;
   HICON GetSmallWindowIcon() const override;
   int GetAppbarAutohideEdges(HMONITOR monitor,
-                             const base::Closure& callback) override;
+                             base::OnceClosure callback) override;
 #elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
   gfx::ImageSkia* GetDefaultWindowIcon() const override;
   bool WindowManagerProvidesTitleBar(bool maximized) override;
@@ -64,7 +64,7 @@
 
   // Callback on main thread with the edges. |returned_edges| is the value that
   // was returned from the call to GetAutohideEdges() that initiated the lookup.
-  void OnGotAppbarAutohideEdges(const base::Closure& callback,
+  void OnGotAppbarAutohideEdges(base::OnceClosure callback,
                                 HMONITOR monitor,
                                 int returned_edges,
                                 int edges);
diff --git a/chrome/browser/ui/views/chrome_views_delegate_win.cc b/chrome/browser/ui/views/chrome_views_delegate_win.cc
index 22eeef5..4ecb7bf 100644
--- a/chrome/browser/ui/views/chrome_views_delegate_win.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate_win.cc
@@ -7,6 +7,8 @@
 #include <dwmapi.h>
 #include <shellapi.h>
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/task/post_task.h"
 #include "base/win/windows_version.h"
@@ -153,7 +155,7 @@
 }
 
 int ChromeViewsDelegate::GetAppbarAutohideEdges(HMONITOR monitor,
-                                                const base::Closure& callback) {
+                                                base::OnceClosure callback) {
   // Initialize the map with EDGE_BOTTOM. This is important, as if we return an
   // initial value of 0 (no auto-hide edges) then we'll go fullscreen and
   // windows will automatically remove WS_EX_TOPMOST from the appbar resulting
@@ -171,23 +173,22 @@
     // https://crbug.com/662122
     base::PostTaskWithTraitsAndReplyWithResult(
         FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
-        base::Bind(&GetAppbarAutohideEdgesOnWorkerThread, monitor),
-        base::Bind(&ChromeViewsDelegate::OnGotAppbarAutohideEdges,
-                   weak_factory_.GetWeakPtr(), callback, monitor,
-                   appbar_autohide_edge_map_[monitor]));
+        base::BindOnce(&GetAppbarAutohideEdgesOnWorkerThread, monitor),
+        base::BindOnce(&ChromeViewsDelegate::OnGotAppbarAutohideEdges,
+                       weak_factory_.GetWeakPtr(), std::move(callback), monitor,
+                       appbar_autohide_edge_map_[monitor]));
   }
   return appbar_autohide_edge_map_[monitor];
 }
 
-void ChromeViewsDelegate::OnGotAppbarAutohideEdges(
-    const base::Closure& callback,
-    HMONITOR monitor,
-    int returned_edges,
-    int edges) {
+void ChromeViewsDelegate::OnGotAppbarAutohideEdges(base::OnceClosure callback,
+                                                   HMONITOR monitor,
+                                                   int returned_edges,
+                                                   int edges) {
   appbar_autohide_edge_map_[monitor] = edges;
   if (returned_edges == edges)
     return;
 
   base::AutoReset<bool> in_callback_setter(&in_autohide_edges_callback_, true);
-  callback.Run();
+  std::move(callback).Run();
 }
diff --git a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
index f7a6fd4d..3df4f18b 100644
--- a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
+++ b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
@@ -34,6 +34,9 @@
 
 void ReopenTabPromoController::ShowPromo() {
   // This shouldn't be called more than once. Check that state is fresh.
+  DCHECK(!show_promo_called_);
+  show_promo_called_ = true;
+
   DCHECK(!is_showing_);
   is_showing_ = true;
 
@@ -69,7 +72,8 @@
   if (command_id == IDC_RESTORE_TAB) {
     // If using the keyboard shortcut, we bypass the other steps and so we close
     // the bubble now.
-    promo_bubble_->GetWidget()->Close();
+    if (promo_bubble_)
+      promo_bubble_->GetWidget()->Close();
     PromoEnded();
   }
 }
@@ -108,4 +112,6 @@
   auto* app_menu_button = browser_view_->toolbar()->app_menu_button();
   app_menu_button->SetPromoFeature(base::nullopt);
   app_menu_button->RemoveObserver(this);
+
+  is_showing_ = false;
 }
diff --git a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
index a70be45..9c75dd2c 100644
--- a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
+++ b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
@@ -70,6 +70,10 @@
   // Whether we are showing the promo.
   bool is_showing_ = false;
 
+  // Whether ShowPromo() has ever been called. It should only ever be called
+  // once.
+  bool show_promo_called_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(ReopenTabPromoController);
 };
 
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index c67f2b1e..bf28dc82 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -546,12 +546,6 @@
   return GetTooltipText(data_.title, data_.alert_state);
 }
 
-bool Tab::GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* origin) const {
-  origin->set_x(title_->x() + 10);
-  origin->set_y(-4);
-  return true;
-}
-
 void Tab::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   node_data->role = ax::mojom::Role::kTab;
   node_data->AddState(ax::mojom::State::kMultiselectable);
diff --git a/chrome/browser/ui/views/tabs/tab.h b/chrome/browser/ui/views/tabs/tab.h
index 28843c3c..a05900b3 100644
--- a/chrome/browser/ui/views/tabs/tab.h
+++ b/chrome/browser/ui/views/tabs/tab.h
@@ -90,8 +90,6 @@
   void OnMouseExited(const ui::MouseEvent& event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
   base::string16 GetTooltipText(const gfx::Point& p) const override;
-  bool GetTooltipTextOrigin(const gfx::Point& p,
-                            gfx::Point* origin) const override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   gfx::Size CalculatePreferredSize() const override;
   void PaintChildren(const views::PaintInfo& info) override;
diff --git a/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.cc
index d5b80457..630825a 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.cc
@@ -7,6 +7,8 @@
 #include <memory>
 #include <utility>
 
+#include "base/logging.h"
+
 AuthenticatorClientPinEntrySheetView::AuthenticatorClientPinEntrySheetView(
     std::unique_ptr<AuthenticatorClientPinEntrySheetModel> sheet_model)
     : AuthenticatorRequestSheetView(std::move(sheet_model)) {
@@ -29,6 +31,7 @@
                 AuthenticatorClientPinEntrySheetModel::Mode::
                     kPinSetup /* show_confirmation_text_field */);
   pin_entry_view_ = view.get();
+  pin_entry_sheet_model()->MaybeShowRetryError();
   return view;
 }
 
@@ -45,7 +48,7 @@
 void AuthenticatorClientPinEntrySheetView::ShowPinError(
     const base::string16& error) {
   if (!pin_entry_view_) {
-    DCHECK(false);
+    NOTREACHED();
     return;
   }
   pin_entry_view_->UpdateError(error);
diff --git a/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_view.cc b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_view.cc
index 99081aa6..e1687af 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_view.cc
@@ -111,6 +111,7 @@
   error_label_->SetVisible(true);
   error_label_->SetText(text);
   error_label_->SizeToPreferredSize();
+  Layout();
 }
 
 void AuthenticatorClientPinEntryView::RequestFocus() {
diff --git a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
index bdff790..9fd6075 100644
--- a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
+++ b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
@@ -131,6 +131,26 @@
               dialog_model,
               AuthenticatorClientPinEntrySheetModel::Mode::kPinSetup));
       break;
+    case Step::kClientPinTapAgain:
+      sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
+          std::make_unique<AuthenticatorClientPinTapAgainSheetModel>(
+              dialog_model));
+      break;
+    case Step::kClientPinErrorSoftBlock:
+      sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
+          AuthenticatorGenericErrorSheetModel::ForClientPinErrorSoftBlock(
+              dialog_model));
+      break;
+    case Step::kClientPinErrorHardBlock:
+      sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
+          AuthenticatorGenericErrorSheetModel::ForClientPinErrorHardBlock(
+              dialog_model));
+      break;
+    case Step::kClientPinErrorAuthenticatorRemoved:
+      sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
+          AuthenticatorGenericErrorSheetModel::
+              ForClientPinErrorAuthenticatorRemoved(dialog_model));
+      break;
     case Step::kNotStarted:
     case Step::kClosed:
       sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc
index 504584ca..fe013ef 100644
--- a/chrome/browser/ui/webauthn/sheet_models.cc
+++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
@@ -173,10 +174,10 @@
 AuthenticatorInsertAndActivateUsbSheetModel::
     AuthenticatorInsertAndActivateUsbSheetModel(
         AuthenticatorRequestDialogModel* dialog_model)
-    : AuthenticatorSheetModelBase(dialog_model) {
-  other_transports_menu_model_ = std::make_unique<OtherTransportsMenuModel>(
-      dialog_model, AuthenticatorTransport::kUsbHumanInterfaceDevice);
-}
+    : AuthenticatorSheetModelBase(dialog_model),
+      other_transports_menu_model_(std::make_unique<OtherTransportsMenuModel>(
+          dialog_model,
+          AuthenticatorTransport::kUsbHumanInterfaceDevice)) {}
 
 AuthenticatorInsertAndActivateUsbSheetModel::
     ~AuthenticatorInsertAndActivateUsbSheetModel() = default;
@@ -551,10 +552,10 @@
 
 AuthenticatorBleActivateSheetModel::AuthenticatorBleActivateSheetModel(
     AuthenticatorRequestDialogModel* dialog_model)
-    : AuthenticatorSheetModelBase(dialog_model) {
-  other_transports_menu_model_ = std::make_unique<OtherTransportsMenuModel>(
-      dialog_model, AuthenticatorTransport::kBluetoothLowEnergy);
-}
+    : AuthenticatorSheetModelBase(dialog_model),
+      other_transports_menu_model_(std::make_unique<OtherTransportsMenuModel>(
+          dialog_model,
+          AuthenticatorTransport::kBluetoothLowEnergy)) {}
 
 AuthenticatorBleActivateSheetModel::~AuthenticatorBleActivateSheetModel() =
     default;
@@ -586,10 +587,10 @@
 
 AuthenticatorTouchIdSheetModel::AuthenticatorTouchIdSheetModel(
     AuthenticatorRequestDialogModel* dialog_model)
-    : AuthenticatorSheetModelBase(dialog_model) {
-  other_transports_menu_model_ = std::make_unique<OtherTransportsMenuModel>(
-      dialog_model, AuthenticatorTransport::kInternal);
-}
+    : AuthenticatorSheetModelBase(dialog_model),
+      other_transports_menu_model_(std::make_unique<OtherTransportsMenuModel>(
+          dialog_model,
+          AuthenticatorTransport::kInternal)) {}
 
 AuthenticatorTouchIdSheetModel::~AuthenticatorTouchIdSheetModel() = default;
 
@@ -626,10 +627,6 @@
 }
 
 ui::MenuModel* AuthenticatorTouchIdSheetModel::GetOtherTransportsMenuModel() {
-  if (!other_transports_menu_model_) {
-    other_transports_menu_model_ = std::make_unique<OtherTransportsMenuModel>(
-        dialog_model(), AuthenticatorTransport::kInternal);
-  }
   return other_transports_menu_model_.get();
 }
 
@@ -637,10 +634,10 @@
 
 AuthenticatorPaaskSheetModel::AuthenticatorPaaskSheetModel(
     AuthenticatorRequestDialogModel* dialog_model)
-    : AuthenticatorSheetModelBase(dialog_model) {
-  other_transports_menu_model_ = std::make_unique<OtherTransportsMenuModel>(
-      dialog_model, AuthenticatorTransport::kCloudAssistedBluetoothLowEnergy);
-}
+    : AuthenticatorSheetModelBase(dialog_model),
+      other_transports_menu_model_(std::make_unique<OtherTransportsMenuModel>(
+          dialog_model,
+          AuthenticatorTransport::kCloudAssistedBluetoothLowEnergy)) {}
 
 AuthenticatorPaaskSheetModel::~AuthenticatorPaaskSheetModel() = default;
 
@@ -691,6 +688,30 @@
   pin_confirmation_ = std::move(pin_confirmation);
 }
 
+void AuthenticatorClientPinEntrySheetModel::MaybeShowRetryError() {
+  if (!delegate_) {
+    NOTREACHED();
+    return;
+  }
+  if (!dialog_model()->has_attempted_pin_entry()) {
+    return;
+  }
+
+  base::string16 error;
+  if (mode_ == AuthenticatorClientPinEntrySheetModel::Mode::kPinEntry) {
+    auto attempts = dialog_model()->pin_attempts();
+    error =
+        attempts && *attempts <= 3
+            ? l10n_util::GetPluralStringFUTF16(
+                  IDS_WEBAUTHN_PIN_ENTRY_ERROR_FAILED_RETRIES, *attempts)
+            : l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_ERROR_FAILED);
+  } else {
+    DCHECK(mode_ == AuthenticatorClientPinEntrySheetModel::Mode::kPinSetup);
+    error = l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_SETUP_ERROR_FAILED);
+  }
+  delegate_->ShowPinError(std::move(error));
+}
+
 gfx::ImageSkia* AuthenticatorClientPinEntrySheetModel::GetStepIllustration()
     const {
   return GetImage(IDR_WEBAUTHN_ILLUSTRATION_PIN);
@@ -763,3 +784,95 @@
     dialog_model()->OnHavePIN(base::UTF16ToUTF8(pin_code_));
   }
 }
+
+// AuthenticatorClientPinTapAgainSheetModel ----------------------
+
+AuthenticatorClientPinTapAgainSheetModel::
+    AuthenticatorClientPinTapAgainSheetModel(
+        AuthenticatorRequestDialogModel* dialog_model)
+    : AuthenticatorSheetModelBase(dialog_model) {}
+
+AuthenticatorClientPinTapAgainSheetModel::
+    ~AuthenticatorClientPinTapAgainSheetModel() = default;
+
+bool AuthenticatorClientPinTapAgainSheetModel::IsActivityIndicatorVisible()
+    const {
+  return true;
+}
+
+gfx::ImageSkia* AuthenticatorClientPinTapAgainSheetModel::GetStepIllustration()
+    const {
+  return GetImage(IDR_WEBAUTHN_ILLUSTRATION_USB);
+}
+
+base::string16 AuthenticatorClientPinTapAgainSheetModel::GetStepTitle() const {
+  return l10n_util::GetStringFUTF16(IDS_WEBAUTHN_GENERIC_TITLE,
+                                    GetRelyingPartyIdString());
+}
+
+base::string16 AuthenticatorClientPinTapAgainSheetModel::GetStepDescription()
+    const {
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_TAP_AGAIN_DESCRIPTION);
+}
+
+// AuthenticatorGenericErrorSheetModel -----------------------------------
+
+// static
+std::unique_ptr<AuthenticatorGenericErrorSheetModel>
+AuthenticatorGenericErrorSheetModel::ForClientPinErrorSoftBlock(
+    AuthenticatorRequestDialogModel* dialog_model) {
+  return base::WrapUnique(new AuthenticatorGenericErrorSheetModel(
+      dialog_model, l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE),
+      l10n_util::GetStringUTF16(
+          IDS_WEBAUTHN_CLIENT_PIN_SOFT_BLOCK_DESCRIPTION)));
+}
+
+// static
+std::unique_ptr<AuthenticatorGenericErrorSheetModel>
+AuthenticatorGenericErrorSheetModel::ForClientPinErrorHardBlock(
+    AuthenticatorRequestDialogModel* dialog_model) {
+  return base::WrapUnique(new AuthenticatorGenericErrorSheetModel(
+      dialog_model, l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE),
+      l10n_util::GetStringUTF16(
+          IDS_WEBAUTHN_CLIENT_PIN_HARD_BLOCK_DESCRIPTION)));
+}
+
+// static
+std::unique_ptr<AuthenticatorGenericErrorSheetModel>
+AuthenticatorGenericErrorSheetModel::ForClientPinErrorAuthenticatorRemoved(
+    AuthenticatorRequestDialogModel* dialog_model) {
+  return base::WrapUnique(new AuthenticatorGenericErrorSheetModel(
+      dialog_model, l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE),
+      l10n_util::GetStringUTF16(
+          IDS_WEBAUTHN_CLIENT_PIN_AUTHENTICATOR_REMOVED_DESCRIPTION)));
+}
+
+AuthenticatorGenericErrorSheetModel::AuthenticatorGenericErrorSheetModel(
+    AuthenticatorRequestDialogModel* dialog_model,
+    base::string16 title,
+    base::string16 description)
+    : AuthenticatorSheetModelBase(dialog_model),
+      title_(std::move(title)),
+      description_(std::move(description)) {}
+
+bool AuthenticatorGenericErrorSheetModel::IsBackButtonVisible() const {
+  return false;
+}
+
+base::string16 AuthenticatorGenericErrorSheetModel::GetCancelButtonLabel()
+    const {
+  return l10n_util::GetStringUTF16(IDS_CLOSE);
+}
+
+gfx::ImageSkia* AuthenticatorGenericErrorSheetModel::GetStepIllustration()
+    const {
+  return GetImage(IDR_WEBAUTHN_ILLUSTRATION_ERROR);
+}
+
+base::string16 AuthenticatorGenericErrorSheetModel::GetStepTitle() const {
+  return title_;
+}
+
+base::string16 AuthenticatorGenericErrorSheetModel::GetStepDescription() const {
+  return description_;
+}
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h
index 8ababa8..e5bccb7 100644
--- a/chrome/browser/ui/webauthn/sheet_models.h
+++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -364,6 +364,7 @@
   void SetDelegate(Delegate* delegate);
   void SetPinCode(base::string16 pin_code);
   void SetPinConfirmation(base::string16 pin_confirmation);
+  void MaybeShowRetryError();
 
   Mode mode() const { return mode_; }
 
@@ -383,4 +384,48 @@
   Delegate* delegate_ = nullptr;
 };
 
+class AuthenticatorClientPinTapAgainSheetModel
+    : public AuthenticatorSheetModelBase {
+ public:
+  explicit AuthenticatorClientPinTapAgainSheetModel(
+      AuthenticatorRequestDialogModel* dialog_model);
+  ~AuthenticatorClientPinTapAgainSheetModel() override;
+
+ private:
+  // AuthenticatorSheetModelBase:
+  bool IsActivityIndicatorVisible() const override;
+  gfx::ImageSkia* GetStepIllustration() const override;
+  base::string16 GetStepTitle() const override;
+  base::string16 GetStepDescription() const override;
+};
+
+// Generic error dialog that can only be dismissed. Backwards navigation is
+// not visible.
+class AuthenticatorGenericErrorSheetModel : public AuthenticatorSheetModelBase {
+ public:
+  static std::unique_ptr<AuthenticatorGenericErrorSheetModel>
+  ForClientPinErrorSoftBlock(AuthenticatorRequestDialogModel* dialog_model);
+  static std::unique_ptr<AuthenticatorGenericErrorSheetModel>
+  ForClientPinErrorHardBlock(AuthenticatorRequestDialogModel* dialog_model);
+  static std::unique_ptr<AuthenticatorGenericErrorSheetModel>
+  ForClientPinErrorAuthenticatorRemoved(
+      AuthenticatorRequestDialogModel* dialog_model);
+
+ private:
+  AuthenticatorGenericErrorSheetModel(
+      AuthenticatorRequestDialogModel* dialog_model,
+      base::string16 title,
+      base::string16 description);
+
+  // AuthenticatorSheetModelBase:
+  bool IsBackButtonVisible() const override;
+  base::string16 GetCancelButtonLabel() const override;
+  gfx::ImageSkia* GetStepIllustration() const override;
+  base::string16 GetStepTitle() const override;
+  base::string16 GetStepDescription() const override;
+
+  base::string16 title_;
+  base::string16 description_;
+};
+
 #endif  // CHROME_BROWSER_UI_WEBAUTHN_SHEET_MODELS_H_
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index d62a50d..3f90052 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -576,6 +576,10 @@
 }
 
 void GaiaScreenHandler::Initialize() {
+  initialized_ = true;
+
+  if (show_when_ready_)
+    ShowGaiaScreenIfReady();
 }
 
 void GaiaScreenHandler::RegisterMessages() {
@@ -1080,7 +1084,7 @@
     const base::Optional<AccountId>& account_id) {
   if (account_id)
     populated_email_ = account_id->GetUserEmail();
-  show_when_dns_and_cookies_cleared_ = true;
+  show_when_ready_ = true;
   if (gaia_silent_load_ && populated_email_.empty()) {
     dns_cleared_ = true;
     cookies_cleared_ = true;
@@ -1114,13 +1118,12 @@
 }
 
 void GaiaScreenHandler::CancelShowGaiaAsync() {
-  show_when_dns_and_cookies_cleared_ = false;
+  show_when_ready_ = false;
 }
 
 void GaiaScreenHandler::ShowGaiaScreenIfReady() {
-  if (!dns_cleared_ || !cookies_cleared_ ||
-      !show_when_dns_and_cookies_cleared_ ||
-      !LoginDisplayHost::default_host()) {
+  if (!dns_cleared_ || !cookies_cleared_ || !initialized_ ||
+      !show_when_ready_ || !LoginDisplayHost::default_host()) {
     return;
   }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
index aa5922f..8ed8521 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
@@ -250,6 +250,9 @@
   // Email to pre-populate with.
   std::string populated_email_;
 
+  // Whether the handler has been initialized.
+  bool initialized_ = false;
+
   // True if dns cache cleanup is done.
   bool dns_cleared_ = false;
 
@@ -260,8 +263,8 @@
   bool cookies_cleared_ = false;
 
   // If true, the sign-in screen will be shown when DNS cache and cookie
-  // clean-up finish.
-  bool show_when_dns_and_cookies_cleared_ = false;
+  // clean-up finish, and the handler is initialized (i.e. the web UI is ready).
+  bool show_when_ready_ = false;
 
   // Has Gaia page silent load been started for the current sign-in attempt?
   bool gaia_silent_load_ = false;
diff --git a/chrome/browser/ui/webui/chromeos/set_time_ui.cc b/chrome/browser/ui/webui/chromeos/set_time_ui.cc
index 1128273..f9c80ba 100644
--- a/chrome/browser/ui/webui/chromeos/set_time_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/set_time_ui.cc
@@ -14,14 +14,15 @@
 #include "base/macros.h"
 #include "base/scoped_observer.h"
 #include "base/values.h"
+#include "chrome/browser/chromeos/set_time_dialog.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/dbus/system_clock/system_clock_client.h"
-#include "chromeos/login/login_state/login_state.h"
 #include "chromeos/settings/timezone_settings.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
@@ -70,7 +71,10 @@
 
   // SystemClockClient::Observer:
   void SystemClockUpdated() override {
-    CallJavascriptFunction("settime.TimeSetter.updateTime");
+    if (chromeos::features::IsSetTimeDialogMd())
+      FireWebUIListener("system-clock-updated");
+    else
+      CallJavascriptFunction("settime.TimeSetter.updateTime");
   }
 
   // UI actually shows real device timezone, but only allows changing the user
@@ -80,7 +84,10 @@
   // system::TimezoneSettings::Observer:
   void TimezoneChanged(const icu::TimeZone& timezone) override {
     base::Value timezone_id(system::TimezoneSettings::GetTimezoneID(timezone));
-    CallJavascriptFunction("settime.TimeSetter.setTimezone", timezone_id);
+    if (chromeos::features::IsSetTimeDialogMd())
+      FireWebUIListener("system-timezone-changed", timezone_id);
+    else
+      CallJavascriptFunction("settime.TimeSetter.setTimezone", timezone_id);
   }
 
   // Handler for Javascript call to set the system clock when the user sets a
@@ -137,12 +144,13 @@
   source->AddLocalizedString("timeLabel", IDS_SET_TIME_TIME_LABEL);
 
   base::DictionaryValue values;
+  // List of list of strings: [[ID, name], [ID, name], ...]
   values.Set("timezoneList", chromeos::system::GetTimezoneList());
 
   // If we are not logged in, we need to show the time zone dropdown.
   // Otherwise, we can leave |currentTimezoneId| blank.
   std::string current_timezone_id;
-  if (!LoginState::Get()->IsUserLoggedIn())
+  if (SetTimeDialog::ShouldShowTimezone())
     CrosSettings::Get()->GetString(kSystemTimezone, &current_timezone_id);
   values.SetString("currentTimezoneId", current_timezone_id);
   values.SetDouble("buildTime", base::GetBuildTime().ToJsTime());
@@ -150,9 +158,19 @@
   source->AddLocalizedStrings(values);
   source->SetJsonPath("strings.js");
 
-  source->AddResourcePath("set_time.css", IDR_SET_TIME_CSS);
-  source->AddResourcePath("set_time.js", IDR_SET_TIME_JS);
-  source->SetDefaultResource(IDR_SET_TIME_HTML);
+  if (chromeos::features::IsSetTimeDialogMd()) {
+    source->UseGzip();
+    source->AddResourcePath("set_time_browser_proxy.html",
+                            IDR_MD_SET_TIME_BROWSER_PROXY_HTML);
+    source->AddResourcePath("set_time_browser_proxy.js",
+                            IDR_MD_SET_TIME_BROWSER_PROXY_JS);
+    source->AddResourcePath("set_time.js", IDR_MD_SET_TIME_JS);
+    source->SetDefaultResource(IDR_MD_SET_TIME_HTML);
+  } else {
+    source->AddResourcePath("set_time.css", IDR_SET_TIME_CSS);
+    source->AddResourcePath("set_time.js", IDR_SET_TIME_JS);
+    source->SetDefaultResource(IDR_SET_TIME_HTML);
+  }
 
   content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source);
 }
diff --git a/chrome/browser/ui/webui/download_internals/download_internals_ui.cc b/chrome/browser/ui/webui/download_internals/download_internals_ui.cc
index 4208aee..9cbcaea 100644
--- a/chrome/browser/ui/webui/download_internals/download_internals_ui.cc
+++ b/chrome/browser/ui/webui/download_internals/download_internals_ui.cc
@@ -36,7 +36,6 @@
   html_source->UseGzip();
 
   Profile* profile = Profile::FromWebUI(web_ui);
-  html_source->AddBoolean("isIncognito", profile->IsOffTheRecord());
 
   content::WebUIDataSource::Add(profile, html_source);
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
index d8f69e3..a0b9428 100644
--- a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/ui/webui/settings/chromeos/crostini_handler.h"
 
 #include <string>
+#include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -22,13 +24,7 @@
 CrostiniHandler::CrostiniHandler(Profile* profile)
     : profile_(profile), weak_ptr_factory_(this) {}
 
-CrostiniHandler::~CrostiniHandler() {
-  if (crostini::CrostiniManager::GetForProfile(profile_)
-          ->HasInstallerViewStatusObserver(this)) {
-    crostini::CrostiniManager::GetForProfile(profile_)
-        ->RemoveInstallerViewStatusObserver(this);
-  }
-}
+CrostiniHandler::~CrostiniHandler() = default;
 
 void CrostiniHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback(
@@ -49,6 +45,14 @@
       base::BindRepeating(&CrostiniHandler::HandleRemoveCrostiniSharedPath,
                           weak_ptr_factory_.GetWeakPtr()));
   web_ui()->RegisterMessageCallback(
+      "getCrostiniSharedUsbDevices",
+      base::BindRepeating(&CrostiniHandler::HandleGetCrostiniSharedUsbDevices,
+                          weak_ptr_factory_.GetWeakPtr()));
+  web_ui()->RegisterMessageCallback(
+      "setCrostiniUsbDeviceShared",
+      base::BindRepeating(&CrostiniHandler::HandleSetCrostiniUsbDeviceShared,
+                          weak_ptr_factory_.GetWeakPtr()));
+  web_ui()->RegisterMessageCallback(
       "exportCrostiniContainer",
       base::BindRepeating(&CrostiniHandler::HandleExportCrostiniContainer,
                           weak_ptr_factory_.GetWeakPtr()));
@@ -61,8 +65,25 @@
       base::BindRepeating(
           &CrostiniHandler::HandleCrostiniInstallerStatusRequest,
           weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CrostiniHandler::OnJavascriptAllowed() {
   crostini::CrostiniManager::GetForProfile(profile_)
       ->AddInstallerViewStatusObserver(this);
+  if (chromeos::CrosUsbDetector::Get()) {
+    chromeos::CrosUsbDetector::Get()->AddSharedUsbDeviceObserver(this);
+  }
+}
+
+void CrostiniHandler::OnJavascriptDisallowed() {
+  if (crostini::CrostiniManager::GetForProfile(profile_)
+          ->HasInstallerViewStatusObserver(this)) {
+    crostini::CrostiniManager::GetForProfile(profile_)
+        ->RemoveInstallerViewStatusObserver(this);
+  }
+  if (chromeos::CrosUsbDetector::Get()) {
+    chromeos::CrosUsbDetector::Get()->RemoveSharedUsbDeviceObserver(this);
+  }
 }
 
 void CrostiniHandler::HandleRequestCrostiniInstallerView(
@@ -113,6 +134,65 @@
           path));
 }
 
+namespace {
+base::ListValue UsbDevicesToListValue(
+    const std::vector<SharedUsbDeviceInfo> shared_usbs) {
+  base::ListValue usb_devices_list;
+  for (auto device : shared_usbs) {
+    base::Value device_info(base::Value::Type::DICTIONARY);
+    device_info.SetKey("guid", base::Value(device.guid));
+    device_info.SetKey("label", base::Value(device.label));
+    device_info.SetKey("shared", base::Value(device.shared));
+    usb_devices_list.GetList().push_back(std::move(device_info));
+  }
+  return usb_devices_list;
+}
+}  // namespace
+
+void CrostiniHandler::HandleGetCrostiniSharedUsbDevices(
+    const base::ListValue* args) {
+  AllowJavascript();
+  CHECK_EQ(1U, args->GetSize());
+
+  std::string callback_id = args->GetList()[0].GetString();
+
+  chromeos::CrosUsbDetector* detector = chromeos::CrosUsbDetector::Get();
+  if (!detector) {
+    ResolveJavascriptCallback(base::Value(callback_id), base::ListValue());
+    return;
+  }
+
+  ResolveJavascriptCallback(
+      base::Value(callback_id),
+      UsbDevicesToListValue(detector->GetSharedUsbDevices()));
+}
+
+void CrostiniHandler::HandleSetCrostiniUsbDeviceShared(
+    const base::ListValue* args) {
+  CHECK_EQ(2U, args->GetSize());
+  const auto& args_list = args->GetList();
+  std::string guid = args_list[0].GetString();
+  bool shared = args_list[1].GetBool();
+
+  chromeos::CrosUsbDetector* detector = chromeos::CrosUsbDetector::Get();
+  if (!detector)
+    return;
+
+  if (shared) {
+    detector->AttachUsbDeviceToVm(crostini::kCrostiniDefaultVmName, guid,
+                                  base::DoNothing());
+    return;
+  }
+  detector->DetachUsbDeviceFromVm(crostini::kCrostiniDefaultVmName, guid,
+                                  base::DoNothing());
+}
+
+void CrostiniHandler::OnSharedUsbDevicesChanged(
+    const std::vector<SharedUsbDeviceInfo> shared_usbs) {
+  FireWebUIListener("crostini-shared-usb-devices-changed",
+                    UsbDevicesToListValue(shared_usbs));
+}
+
 void CrostiniHandler::HandleExportCrostiniContainer(
     const base::ListValue* args) {
   CHECK_EQ(0U, args->GetSize());
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h
index 6358b30e..cda03463 100644
--- a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h
@@ -5,8 +5,11 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CROSTINI_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CROSTINI_HANDLER_H_
 
+#include <vector>
+
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
+#include "chrome/browser/chromeos/usb/cros_usb_detector.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 
 class Profile;
@@ -19,15 +22,16 @@
 namespace settings {
 
 class CrostiniHandler : public ::settings::SettingsPageUIHandler,
-                        public crostini::InstallerViewStatusObserver {
+                        public crostini::InstallerViewStatusObserver,
+                        public chromeos::SharedUsbDeviceObserver {
  public:
   explicit CrostiniHandler(Profile* profile);
   ~CrostiniHandler() override;
 
   // SettingsPageUIHandler
   void RegisterMessages() override;
-  void OnJavascriptAllowed() override {}
-  void OnJavascriptDisallowed() override {}
+  void OnJavascriptAllowed() override;
+  void OnJavascriptDisallowed() override;
 
  private:
   void HandleRequestCrostiniInstallerView(const base::ListValue* args);
@@ -38,6 +42,13 @@
   void HandleGetCrostiniSharedPathsDisplayText(const base::ListValue* args);
   // Remove a specified path from being shared.
   void HandleRemoveCrostiniSharedPath(const base::ListValue* args);
+  // Returns a list of available USB devices.
+  void HandleGetCrostiniSharedUsbDevices(const base::ListValue* args);
+  // Set the share state of a USB device.
+  void HandleSetCrostiniUsbDeviceShared(const base::ListValue* args);
+  // chromeos::SharedUsbDeviceObserver.
+  void OnSharedUsbDevicesChanged(
+      const std::vector<SharedUsbDeviceInfo> shared_usbs) override;
   // Export the crostini container.
   void HandleExportCrostiniContainer(const base::ListValue* args);
   // Import the crostini container.
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 4422418c..049f08da 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -465,8 +465,10 @@
        IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LABEL},
       {"crostiniSharedUsbDevicesDescription",
        IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_DESCRIPTION},
-      {"crostiniSharedUsbDevicesListHeading",
-       IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LIST_HEADING},
+      {"crostiniSharedUsbDevicesExtraDescription",
+       IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_EXTRA_DESCRIPTION},
+      {"crostiniSharedUsbDevicesListEmptyMessage",
+       IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LIST_EMPTY_MESSAGE},
   };
   AddLocalizedStringsBulk(html_source, kLocalizedStrings,
                           base::size(kLocalizedStrings));
diff --git a/chrome/browser/vr/service/xr_runtime_manager.cc b/chrome/browser/vr/service/xr_runtime_manager.cc
index 86bdc18..a7c69ba 100644
--- a/chrome/browser/vr/service/xr_runtime_manager.cc
+++ b/chrome/browser/vr/service/xr_runtime_manager.cc
@@ -190,8 +190,9 @@
       return orientation_runtime;
     }
 
-    // Otherwise fall back to immersive providers.
-    return GetImmersiveRuntime();
+    // If we don't have an orientation provider, then we don't have an explicit
+    // runtime to back a non-immersive session
+    return nullptr;
   }
 }
 
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index b87c9d5..633134e5 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -379,11 +379,15 @@
 }
 
 void AuthenticatorRequestDialogModel::OnSoftPINBlock() {
-  // TODO
+  SetCurrentStep(Step::kClientPinErrorSoftBlock);
 }
 
 void AuthenticatorRequestDialogModel::OnHardPINBlock() {
-  // TODO
+  SetCurrentStep(Step::kClientPinErrorHardBlock);
+}
+
+void AuthenticatorRequestDialogModel::OnAuthenticatorRemovedDuringPINEntry() {
+  SetCurrentStep(Step::kClientPinErrorAuthenticatorRemoved);
 }
 
 void AuthenticatorRequestDialogModel::OnBluetoothPoweredStateChanged(
@@ -438,10 +442,14 @@
 }
 
 void AuthenticatorRequestDialogModel::OnHavePIN(const std::string& pin) {
-  // TODO: disable the PIN submission action once activated. Otherwise
-  // |OnHavePIN| may be called twice because they'll be a delay between
-  // submitted the PIN and figuring out whether it's valid or not.
+  if (!pin_callback_) {
+    // Protect against the view submitting a PIN more than once without
+    // receiving a matching response first. |SetPINCallback| is called again if
+    // the user needs to be prompted for a retry.
+    return;
+  }
   std::move(pin_callback_).Run(pin);
+  has_attempted_pin_entry_ = true;
 }
 
 void AuthenticatorRequestDialogModel::AddAuthenticator(
@@ -506,3 +514,14 @@
   saved_authenticators_.AddAuthenticator(std::move(test_authenticator));
 }
 
+void AuthenticatorRequestDialogModel::CollectPIN(
+    base::Optional<int> attempts,
+    base::OnceCallback<void(std::string)> provide_pin_cb) {
+  pin_callback_ = std::move(provide_pin_cb);
+  if (attempts) {
+    pin_attempts_ = attempts;
+    SetCurrentStep(Step::kClientPinEntry);
+  } else {
+    SetCurrentStep(Step::kClientPinSetup);
+  }
+}
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h
index 5112777..ed38515d 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.h
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -83,6 +83,10 @@
     // Authenticator Client PIN.
     kClientPinEntry,
     kClientPinSetup,
+    kClientPinTapAgain,
+    kClientPinErrorSoftBlock,
+    kClientPinErrorHardBlock,
+    kClientPinErrorAuthenticatorRemoved,
   };
 
   // Implemented by the dialog to observe this model and show the UI panels
@@ -271,6 +275,10 @@
   // performing any PIN operations because of too many failures.
   void OnHardPINBlock();
 
+  // To be called when the selected authenticator was removed while
+  // waiting for a PIN to be entered.
+  void OnAuthenticatorRemovedDuringPINEntry();
+
   // To be called when the Bluetooth adapter powered state changes.
   void OnBluetoothPoweredStateChanged(bool powered);
 
@@ -308,6 +316,11 @@
     return available_transports_;
   }
 
+  void CollectPIN(base::Optional<int> attempts,
+                  base::OnceCallback<void(std::string)> provide_pin_cb);
+  bool has_attempted_pin_entry() const { return has_attempted_pin_entry_; }
+  base::Optional<int> pin_attempts() const { return pin_attempts_; }
+
  private:
   void DispatchRequestAsync(AuthenticatorReference* authenticator,
                             base::TimeDelta delay);
@@ -347,7 +360,10 @@
   BlePairingCallback ble_pairing_callback_;
   base::RepeatingClosure bluetooth_adapter_power_on_callback_;
   BleDevicePairedCallback ble_device_paired_callback_;
+
   base::OnceCallback<void(std::string)> pin_callback_;
+  bool has_attempted_pin_entry_ = false;
+  base::Optional<int> pin_attempts_;
 
   base::WeakPtrFactory<AuthenticatorRequestDialogModel> weak_factory_;
 
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index b4c83f3..d69a1a0 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -140,6 +140,9 @@
     case InterestingFailureReason::kHardPINBlock:
       weak_dialog_model_->OnHardPINBlock();
       break;
+    case InterestingFailureReason::kAuthenticatorRemovedDuringPINEntry:
+      weak_dialog_model_->OnAuthenticatorRemovedDuringPINEntry();
+      break;
   }
   return true;
 }
@@ -405,20 +408,12 @@
   if (!weak_dialog_model_)
     return;
 
-  weak_dialog_model_->SetPINCallback(std::move(provide_pin_cb));
-  if (attempts) {
-    weak_dialog_model_->SetCurrentStep(
-        AuthenticatorRequestDialogModel::Step::kClientPinEntry);
-  } else {
-    weak_dialog_model_->SetCurrentStep(
-        AuthenticatorRequestDialogModel::Step::kClientPinSetup);
-  }
+  weak_dialog_model_->CollectPIN(attempts, std::move(provide_pin_cb));
 }
 
 void ChromeAuthenticatorRequestDelegate::FinishCollectPIN() {
-  // TODO: add a distinct step for this.
   weak_dialog_model_->SetCurrentStep(
-      AuthenticatorRequestDialogModel::Step::kUsbInsertAndActivate);
+      AuthenticatorRequestDialogModel::Step::kClientPinTapAgain);
 }
 
 void ChromeAuthenticatorRequestDelegate::OnModelDestroyed() {
diff --git a/chrome/test/chromedriver/net/net_util_unittest.cc b/chrome/test/chromedriver/net/net_util_unittest.cc
index de666dff..7ec19f1 100644
--- a/chrome/test/chromedriver/net/net_util_unittest.cc
+++ b/chrome/test/chromedriver/net/net_util_unittest.cc
@@ -117,8 +117,7 @@
 
   void OnWebSocketRequest(int connection_id,
                           const net::HttpServerRequestInfo& info) override {}
-  void OnWebSocketMessage(int connection_id, const std::string& data) override {
-  }
+  void OnWebSocketMessage(int connection_id, std::string data) override {}
   void OnClose(int connection_id) override {}
 
  protected:
diff --git a/chrome/test/chromedriver/net/test_http_server.cc b/chrome/test/chromedriver/net/test_http_server.cc
index 235b260..99e0ef0 100644
--- a/chrome/test/chromedriver/net/test_http_server.cc
+++ b/chrome/test/chromedriver/net/test_http_server.cc
@@ -115,8 +115,7 @@
   }
 }
 
-void TestHttpServer::OnWebSocketMessage(int connection_id,
-                                        const std::string& data) {
+void TestHttpServer::OnWebSocketMessage(int connection_id, std::string data) {
   WebSocketMessageAction action;
   base::Closure callback;
   {
diff --git a/chrome/test/chromedriver/net/test_http_server.h b/chrome/test/chromedriver/net/test_http_server.h
index 570e4b2..3d83b97 100644
--- a/chrome/test/chromedriver/net/test_http_server.h
+++ b/chrome/test/chromedriver/net/test_http_server.h
@@ -69,7 +69,7 @@
                      const net::HttpServerRequestInfo& info) override {}
   void OnWebSocketRequest(int connection_id,
                           const net::HttpServerRequestInfo& info) override;
-  void OnWebSocketMessage(int connection_id, const std::string& data) override;
+  void OnWebSocketMessage(int connection_id, std::string data) override;
   void OnClose(int connection_id) override;
 
  private:
diff --git a/chrome/test/chromedriver/server/chromedriver_server.cc b/chrome/test/chromedriver/server/chromedriver_server.cc
index 43921dc..b72bf79 100644
--- a/chrome/test/chromedriver/server/chromedriver_server.cc
+++ b/chrome/test/chromedriver/server/chromedriver_server.cc
@@ -166,8 +166,7 @@
   }
   void OnWebSocketRequest(int connection_id,
                           const net::HttpServerRequestInfo& info) override {}
-  void OnWebSocketMessage(int connection_id, const std::string& data) override {
-  }
+  void OnWebSocketMessage(int connection_id, std::string data) override {}
   void OnClose(int connection_id) override {}
 
  private:
diff --git a/chrome/test/data/chromeos/file_manager/archive.zip b/chrome/test/data/chromeos/file_manager/archive.zip
index 92da528..054b7c2a 100644
--- a/chrome/test/data/chromeos/file_manager/archive.zip
+++ b/chrome/test/data/chromeos/file_manager/archive.zip
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/autotest_private/test.js b/chrome/test/data/extensions/api_test/autotest_private/test.js
index 895febc..ba8d5b8 100644
--- a/chrome/test/data/extensions/api_test/autotest_private/test.js
+++ b/chrome/test/data/extensions/api_test/autotest_private/test.js
@@ -234,21 +234,21 @@
   function setAssistantEnabled() {
     chrome.autotestPrivate.setAssistantEnabled(true, 1000 /* timeout_ms */,
         chrome.test.callbackFail(
-            'Assistant is not available for the current user'));
+            'Assistant not allowed - state: 4'));
   },
   function sendAssistantTextQuery() {
     chrome.autotestPrivate.sendAssistantTextQuery(
         'what time is it?' /* query */,
         1000 /* timeout_ms */,
         chrome.test.callbackFail(
-          'Assistant is not available for the current user'));
+            'Assistant not allowed - state: 4'));
   },
   function setWhitelistedPref() {
     chrome.autotestPrivate.setWhitelistedPref(
         'settings.voice_interaction.hotword.enabled' /* pref_name */,
         true /* value */,
         chrome.test.callbackFail(
-          'Assistant is not available for the current user'));
+            'Assistant not allowed - state: 4'));
   },
   // This test verifies that getArcState returns provisioned False in case ARC
   // is not provisioned by default.
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index e4d4a05e..32faf5c 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -102,6 +102,7 @@
   if (is_chromeos) {
     sources += [
       "certificate_viewer_dialog_test.js",
+      "md_set_time_browsertest.js",
       "sys_internals/sys_internals_browsertest.js",
     ]
   } else {
diff --git a/chrome/test/data/webui/extensions/activity_log_stream_item_test.js b/chrome/test/data/webui/extensions/activity_log_stream_item_test.js
index 5cb94da2..6ec5d12e 100644
--- a/chrome/test/data/webui/extensions/activity_log_stream_item_test.js
+++ b/chrome/test/data/webui/extensions/activity_log_stream_item_test.js
@@ -27,6 +27,7 @@
       activityType: chrome.activityLogPrivate.ExtensionActivityType.API_CALL,
       pageUrl: '',
       args: JSON.stringify([]),
+      expanded: false
     };
 
     activityLogStreamItem = new extensions.ActivityLogStreamItem();
@@ -57,7 +58,8 @@
               chrome.activityLogPrivate.ExtensionActivityType.API_CALL,
           pageUrl: 'example.url',
           args: JSON.stringify([null]),
-          webRequestInfo: 'web request info'
+          webRequestInfo: 'web request info',
+          expanded: false
         };
 
         activityLogStreamItem.set('data', testStreamItem);
@@ -91,6 +93,7 @@
         ["${placeholder}"],
         {"url":"${escapedPlaceholder}"}
       ]`,
+      expanded: false
     };
 
     activityLogStreamItem.set('data', testStreamItem);
diff --git a/chrome/test/data/webui/md_set_time_browsertest.js b/chrome/test/data/webui/md_set_time_browsertest.js
new file mode 100644
index 0000000..45b5e61
--- /dev/null
+++ b/chrome/test/data/webui/md_set_time_browsertest.js
@@ -0,0 +1,119 @@
+// 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.
+
+/** @const {string} Path to source root. */
+const ROOT_PATH = '../../../../';
+
+GEN_INCLUDE(
+    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN('#include "chromeos/constants/chromeos_features.h"');
+
+/**
+ * MdSetTimeBrowserTest tests loading and interacting with the Material Design
+ * version of the "Set Time" web UI, which is normally shown as a dialog.
+ * @constructor
+ * @extends {PolymerTest}
+ */
+function MdSetTimeBrowserTest() {}
+
+MdSetTimeBrowserTest.prototype = {
+  __proto__: PolymerTest.prototype,
+
+  browsePreload: 'chrome://set-time/',
+
+  featureList: ['chromeos::features::kSetTimeDialogMd', ''],
+
+  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+    ROOT_PATH + 'chrome/test/data/webui/test_browser_proxy.js',
+  ]),
+};
+
+TEST_F('MdSetTimeBrowserTest', 'All', function() {
+  suite('SetTime', function() {
+    let setTimeElement = null;
+    let testBrowserProxy = null;
+
+    /** @implements {settime.SetTimeBrowserProxy} */
+    class TestSetTimeBrowserProxy extends TestBrowserProxy {
+      constructor() {
+        super([
+          'sendPageReady',
+          'setTimeInSeconds',
+          'setTimezone',
+          'dialogClose',
+        ]);
+      }
+
+      /** @override */
+      sendPageReady() {
+        this.methodCalled('sendPageReady');
+      }
+
+      /** @override */
+      setTimeInSeconds(timeInSeconds) {
+        this.methodCalled('setTimeInSeconds', timeInSeconds);
+      }
+
+      /** @override */
+      setTimezone(timezone) {
+        this.methodCalled('setTimezone');
+      }
+
+      /** @override */
+      dialogClose() {
+        this.methodCalled('dialogClose');
+      }
+    }
+
+    setup(function() {
+      testBrowserProxy = new TestSetTimeBrowserProxy();
+      settime.SetTimeBrowserProxyImpl.instance_ = testBrowserProxy;
+      PolymerTest.clearBody();
+      setTimeElement = document.createElement('set-time');
+      document.body.appendChild(setTimeElement);
+    });
+
+    teardown(function() {
+      setTimeElement.remove();
+    });
+
+    test('PageReady', () => {
+      // Verify the page sends the ready message.
+      assertEquals(1, testBrowserProxy.getCallCount('sendPageReady'));
+    });
+
+    test('SetDate', () => {
+      const dateInput = setTimeElement.$$('#dateInput');
+      assertTrue(!!dateInput);
+
+      // Simulate the user changing the date picker forward by a week.
+      const today = dateInput.valueAsDate;
+      const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);
+      dateInput.focus();
+      dateInput.valueAsDate = nextWeek;
+      dateInput.blur();
+
+      // Verify the page sends a request to move time forward.
+      return testBrowserProxy.whenCalled('setTimeInSeconds')
+          .then(timeInSeconds => {
+            const todaySeconds = today.getTime() / 1000;
+            // The exact value isn't important (it depends on the current time).
+            assertGT(timeInSeconds, todaySeconds);
+          });
+    });
+
+    test('SystemTimezoneChanged', () => {
+      const timezoneSelect = setTimeElement.$$('#timezoneSelect');
+      assertTrue(!!timezoneSelect);
+
+      cr.webUIListenerCallback('system-timezone-changed', 'America/New_York');
+      expectEquals('America/New_York', timezoneSelect.value);
+
+      cr.webUIListenerCallback('system-timezone-changed', 'Europe/Moscow');
+      expectEquals('Europe/Moscow', timezoneSelect.value);
+    });
+  });
+
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
index dbd57b9..21ffd94 100644
--- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -384,6 +384,10 @@
   this.runMochaTest(preview_generation_test.TestNames.Margins);
 });
 
+TEST_F('PrintPreviewPreviewGenerationTest', 'CustomMargins', function() {
+  this.runMochaTest(preview_generation_test.TestNames.CustomMargins);
+});
+
 TEST_F('PrintPreviewPreviewGenerationTest', 'MediaSize', function() {
   this.runMochaTest(preview_generation_test.TestNames.MediaSize);
 });
diff --git a/chrome/test/data/webui/print_preview/preview_generation_test.js b/chrome/test/data/webui/print_preview/preview_generation_test.js
index 157e686c..ce64267 100644
--- a/chrome/test/data/webui/print_preview/preview_generation_test.js
+++ b/chrome/test/data/webui/print_preview/preview_generation_test.js
@@ -11,6 +11,7 @@
     HeaderFooter: 'header/footer',
     Layout: 'layout',
     Margins: 'margins',
+    CustomMargins: 'custom margins',
     MediaSize: 'media size',
     PageRange: 'page range',
     Rasterize: 'rasterize',
@@ -65,10 +66,12 @@
             nativeLayer.whenCalled('getPrinterCapabilities'),
           ])
           .then(function() {
-            if (!page.documentSettings_.isModifiable) {
-              page.documentSettings_.fitToPageScaling = 98;
+            const documentInfo = page.$$('print-preview-document-info');
+            if (!documentInfo.documentSettings.isModifiable) {
+              documentInfo.documentSettings.fitToPageScaling = 98;
             }
-            page.documentSettings_.pageCount = 3;
+            documentInfo.documentSettings.pageCount = 3;
+            documentInfo.margins = new print_preview.Margins(10, 10, 10, 10);
             return nativeLayer.whenCalled('getPreview');
           });
     }
@@ -153,6 +156,79 @@
     });
 
     /**
+     * Validate changing the custom margins updates the preview, only after all
+     * values have been set.
+     */
+    test(assert(TestNames.CustomMargins), function() {
+      return initialize()
+          .then(function(args) {
+            const originalTicket = JSON.parse(args.printTicket);
+            assertEquals(
+                print_preview.ticket_items.MarginsTypeValue.DEFAULT,
+                originalTicket.marginsType);
+            // Custom margins should not be set in the ticket.
+            assertEquals(undefined, originalTicket.marginsCustom);
+            assertEquals(0, originalTicket.requestID);
+
+            // This should do nothing.
+            page.setSetting(
+                'margins', print_preview.ticket_items.MarginsTypeValue.CUSTOM);
+            // Sets only 1 side, not valid.
+            page.setSetting('customMargins', {marginTop: 25});
+            // 2 sides, still not valid.
+            page.setSetting('customMargins', {marginTop: 25, marginRight: 40});
+            // This should trigger a preview.
+            nativeLayer.resetResolver('getPreview');
+            page.setSetting('customMargins', {
+              marginTop: 25,
+              marginRight: 40,
+              marginBottom: 20,
+              marginLeft: 50
+            });
+            return nativeLayer.whenCalled('getPreview');
+          })
+          .then(function(args) {
+            const ticket = JSON.parse(args.printTicket);
+            assertEquals(
+                print_preview.ticket_items.MarginsTypeValue.CUSTOM,
+                ticket.marginsType);
+            assertEquals(25, ticket.marginsCustom.marginTop);
+            assertEquals(40, ticket.marginsCustom.marginRight);
+            assertEquals(20, ticket.marginsCustom.marginBottom);
+            assertEquals(50, ticket.marginsCustom.marginLeft);
+            assertEquals(1, ticket.requestID);
+            page.setSetting(
+                'margins', print_preview.ticket_items.MarginsTypeValue.DEFAULT);
+            // Set setting to something invalid and then set margins to CUSTOM.
+            page.setSetting('customMargins', {marginTop: 25, marginRight: 40});
+            page.setSetting(
+                'margins', print_preview.ticket_items.MarginsTypeValue.CUSTOM);
+            nativeLayer.resetResolver('getPreview');
+            page.setSetting('customMargins', {
+              marginTop: 25,
+              marginRight: 40,
+              marginBottom: 20,
+              marginLeft: 50
+            });
+            return nativeLayer.whenCalled('getPreview');
+          })
+          .then(function(args) {
+            const ticket = JSON.parse(args.printTicket);
+            assertEquals(
+                print_preview.ticket_items.MarginsTypeValue.CUSTOM,
+                ticket.marginsType);
+            assertEquals(25, ticket.marginsCustom.marginTop);
+            assertEquals(40, ticket.marginsCustom.marginRight);
+            assertEquals(20, ticket.marginsCustom.marginBottom);
+            assertEquals(50, ticket.marginsCustom.marginLeft);
+            // Request 3. Changing to default margins should have triggered a
+            // preview, and the final setting of valid custom margins should
+            // have triggered another one.
+            assertEquals(3, ticket.requestID);
+          });
+    });
+
+    /**
      * Validate changing the pages per sheet updates the preview, and resets
      * margins to print_preview.ticket_items.MarginsTypeValue.DEFAULT.
      */
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index c9a8965..9e316b4 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -10,6 +10,11 @@
 // Polymer BrowserTest fixture.
 GEN_INCLUDE(
     [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+
+GEN('#if defined(OS_CHROMEOS)');
+GEN('#include "ash/public/cpp/ash_features.h"');
+GEN('#endif  // defined(OS_CHROMEOS)');
+
 GEN('#include "chrome/common/chrome_features.h"');
 GEN('#include "components/autofill/core/common/autofill_features.h"');
 GEN('#include "components/omnibox/common/omnibox_features.h"');
@@ -2179,6 +2184,39 @@
 });
 
 /**
+ * Test fixture for the Chrome OS Kiosk Next Shell page.
+ * @constructor
+ * @extends {CrSettingsBrowserTest}
+ */
+function CrSettingsKioskNextShellPageTest() {}
+
+CrSettingsKioskNextShellPageTest.prototype = {
+  __proto__: CrSettingsBrowserTest.prototype,
+
+  /** @override */
+  browsePreload:
+      'chrome://settings/kiosk_next_shell_page/kiosk_next_shell_page.html',
+
+  /** @override */
+  featureList: ['ash::features::kKioskNextShell', ''],
+
+  extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
+    '../test_browser_proxy.js',
+    'kiosk_next_shell_page_tests.js',
+    'test_util.js',
+    'test_lifetime_browser_proxy.js',
+  ]),
+};
+
+GEN('#if defined(OS_CHROMEOS)');
+GEN('#if defined(GOOGLE_CHROME_BUILD)');
+TEST_F('CrSettingsKioskNextShellPageTest', 'All', function() {
+  mocha.run();
+});
+GEN('#endif  // defined(GOOGLE_CHROME_BUILD)');
+GEN('#endif  // defined(OS_CHROMEOS)');
+
+/**
  * Test fixture for the Linux for Chromebook (Crostini) page.
  * @constructor
  * @extends {CrSettingsBrowserTest}
diff --git a/chrome/test/data/webui/settings/crostini_page_test.js b/chrome/test/data/webui/settings/crostini_page_test.js
index 2663fc4..f6aca1a 100644
--- a/chrome/test/data/webui/settings/crostini_page_test.js
+++ b/chrome/test/data/webui/settings/crostini_page_test.js
@@ -13,7 +13,6 @@
     crostini: {
       enabled: {value: enabled},
       shared_paths: {value: opt_sharedPaths || []},
-      shared_usb_devices: {value: opt_sharedUsbDevices || []},
     }
   };
   crostiniBrowserProxy.enabled = enabled;
@@ -231,6 +230,7 @@
         {'shared': false, 'guid': '0002', 'name': 'usb_dev2'},
         {'shared': true, 'guid': '0003', 'name': 'usb_dev3'}
       ]);
+
       return flushAsync()
           .then(() => {
             settings.navigateTo(settings.routes.CROSTINI_SHARED_USB_DEVICES);
@@ -246,21 +246,26 @@
       assertEquals(3, subpage.shadowRoot.querySelectorAll('.toggle').length);
     });
 
-    test('USB shared pref is updated by toggling', function() {
+    test('USB shared state is updated by toggling', function() {
       assertTrue(!!subpage.$$('.toggle'));
       subpage.$$('.toggle').click();
       return flushAsync()
           .then(() => {
             Polymer.dom.flush();
-            assertEquals(
-                crostiniBrowserProxy.sharedUsbDevices[0].shared, false);
-
-            subpage.$$('.toggle').click();
-            return flushAsync();
+            return crostiniBrowserProxy.whenCalled(
+                'setCrostiniUsbDeviceShared');
           })
-          .then(() => {
+          .then(args => {
+            assertEquals('0001', args[0]);
+            assertEquals(false, args[1]);
+
+            // Simulate a change in the underlying model.
+            cr.webUIListenerCallback('crostini-shared-usb-devices-changed', [
+              {'shared': true, 'guid': '0001', 'name': 'usb_dev1'},
+            ]);
             Polymer.dom.flush();
-            assertEquals(crostiniBrowserProxy.sharedUsbDevices[0].shared, true);
+            assertEquals(
+                1, subpage.shadowRoot.querySelectorAll('.toggle').length);
           });
     });
   });
diff --git a/chrome/test/data/webui/settings/kiosk_next_shell_page_tests.js b/chrome/test/data/webui/settings/kiosk_next_shell_page_tests.js
new file mode 100644
index 0000000..6bfca53
--- /dev/null
+++ b/chrome/test/data/webui/settings/kiosk_next_shell_page_tests.js
@@ -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.
+
+suite('KioskNextShellPageTests', function() {
+  /** @type {?SettingsKioskNextShellPageElement} */
+  let page = null;
+
+  /** @type {?settings.TestLifetimeBrowserProxy} */
+  let lifetimeBrowserProxy = null;
+
+  function setKioskNextShellPrefs(enabled) {
+    page.prefs = {
+      ash: {
+        kiosk_next_shell: {
+          enabled: {value: enabled},
+        },
+      },
+    };
+    Polymer.dom.flush();
+  }
+
+  function showDialog() {
+    const dialogButton = page.$$('paper-button');
+    assertTrue(!!dialogButton);
+    dialogButton.click();
+    Polymer.dom.flush();
+
+    const dialog = page.$$('#dialog');
+    assertTrue(!!dialog);
+    assertTrue(dialog.$$('cr-dialog').open);
+    return dialog;
+  }
+
+  setup(function() {
+    lifetimeBrowserProxy = new settings.TestLifetimeBrowserProxy();
+    settings.LifetimeBrowserProxyImpl.instance_ = lifetimeBrowserProxy;
+
+    PolymerTest.clearBody();
+    page = document.createElement('settings-kiosk-next-shell-page');
+    document.body.appendChild(page);
+  });
+
+  teardown(function() {
+    page.remove();
+    page = null;
+  });
+
+  test('kiosk next shell dialog can be canceled', function() {
+    setKioskNextShellPrefs(false);
+    const dialog = showDialog();
+
+    const cancelButton = dialog.$$('#cancel');
+    assertTrue(!!cancelButton);
+    cancelButton.click();
+    Polymer.dom.flush();
+
+    // The value of the pref must still be the same and we shouldn't sign out.
+    assertFalse(page.prefs.ash.kiosk_next_shell.enabled.value);
+    assertEquals(0, lifetimeBrowserProxy.getCallCount('signOutAndRestart'));
+  });
+
+  test('turn on kiosk next shell', function() {
+    setKioskNextShellPrefs(false);
+    const dialog = showDialog();
+
+    const confirmButton = dialog.$$('#confirm');
+    assertTrue(!!confirmButton);
+    confirmButton.click();
+
+    assertTrue(page.prefs.ash.kiosk_next_shell.enabled.value);
+    return lifetimeBrowserProxy.whenCalled('signOutAndRestart');
+  });
+
+  test('turn off kiosk next shell', function() {
+    setKioskNextShellPrefs(true);
+    const dialog = showDialog();
+
+    const confirmButton = dialog.$$('#confirm');
+    assertTrue(!!confirmButton);
+    confirmButton.click();
+
+    assertFalse(page.prefs.ash.kiosk_next_shell.enabled.value);
+    return lifetimeBrowserProxy.whenCalled('signOutAndRestart');
+  });
+});
diff --git a/chrome/test/data/webui/settings/test_crostini_browser_proxy.js b/chrome/test/data/webui/settings/test_crostini_browser_proxy.js
index 6be2f10..a3f8597 100644
--- a/chrome/test/data/webui/settings/test_crostini_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_crostini_browser_proxy.js
@@ -9,12 +9,15 @@
       'requestCrostiniInstallerView',
       'requestRemoveCrostini',
       'getCrostiniSharedPathsDisplayText',
+      'getCrostiniSharedUsbDevices',
+      'setCrostiniUsbDeviceShared',
       'removeCrostiniSharedPath',
       'exportCrostiniContainer',
       'importCrostiniContainer',
     ]);
     this.enabled = false;
     this.sharedPaths = ['path1', 'path2'];
+    this.sharedUsbDevices = [];
   }
 
   /** @override */
@@ -35,6 +38,17 @@
     return Promise.resolve(paths.map(path => path + '-displayText'));
   }
 
+  /** @override */
+  getCrostiniSharedUsbDevices() {
+    this.methodCalled('getCrostiniSharedUsbDevices');
+    return Promise.resolve(this.sharedUsbDevices);
+  }
+
+  /** @override */
+  setCrostiniUsbDeviceShared(guid, shared) {
+    this.methodCalled('setCrostiniUsbDeviceShared', [guid, shared]);
+  }
+
   /** override */
   removeCrostiniSharedPath(path) {
     this.sharedPaths = this.sharedPaths.filter(p => p !== path);
diff --git a/chrome/test/data/xr/e2e_test_files/html/test_inline_vr_poses.html b/chrome/test/data/xr/e2e_test_files/html/test_inline_vr_poses.html
index ed057ca..83c19a38 100644
--- a/chrome/test/data/xr/e2e_test_files/html/test_inline_vr_poses.html
+++ b/chrome/test/data/xr/e2e_test_files/html/test_inline_vr_poses.html
@@ -11,10 +11,22 @@
   </head>
   <body>
     <canvas id="webgl-canvas"></canvas>
+    <script>
+      let shouldDeferNonImmersiveSessionCreation = true;
+    </script>
     <script src="../../../../../../third_party/blink/web_tests/resources/testharness.js"></script>
     <script src="../resources/webxr_e2e.js"></script>
     <script src="../resources/webxr_boilerplate.js"></script>
     <script>
+      // Override the reference space map to only request eye-level stationary
+      // reference spaces, as opposed to the identity MagicWindow/AR spaces.
+      referenceSpaceMap = {
+        [sessionTypes.IMMERSIVE]: { type: 'stationary', subtype: 'eye-level' },
+        [sessionTypes.MAGIC_WINDOW]: { type: 'stationary', subtype: 'eye-level' },
+        [sessionTypes.AR]: { type: 'stationary', subtype: 'eye-level' }
+      }
+      requestMagicWindowSession();
+
       let MAX_FRAME_CALLBACKS = 10;
 
       let num_frame_callbacks = 0;
diff --git a/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js b/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
index f12fd36..d599e2e 100644
--- a/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
+++ b/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
@@ -31,6 +31,12 @@
 });
 var sessionTypeToRequest = sessionTypes.IMMERSIVE;
 
+var referenceSpaceMap = {
+  [sessionTypes.IMMERSIVE]: { type: 'stationary', subtype: 'eye-level' },
+  [sessionTypes.MAGIC_WINDOW]: { type: 'identity' },
+  [sessionTypes.AR]: { type: 'identity' }
+}
+
 class SessionInfo {
   constructor() {
     this.session = null;
@@ -134,7 +140,7 @@
   }
 
   session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
-  session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' })
+  session.requestReferenceSpace(referenceSpaceMap[getSessionType(session)])
       .then( (refSpace) => {
         sessionInfos[getSessionType(session)].currentRefSpace = refSpace;
         session.requestAnimationFrame(onXRFrame);
@@ -188,11 +194,24 @@
   hasPresentedFrame = true;
 }
 
-if (navigator.xr) {
-
+function requestMagicWindowSession() {
   // Set up an inline session (magic window) drawing into the full screen canvas
   // on the page
   let ctx = webglCanvas.getContext('xrpresent');
+  navigator.xr.requestSession()
+  .then((session) => {
+    session.updateRenderState({
+      outputContext: ctx
+    });
+    onSessionStarted(session);
+  })
+  .then( () => {
+    initializationSteps['magicWindowStarted'] = true;
+  });
+}
+
+if (navigator.xr) {
+
   // WebXR for VR tests want an inline session to be automatically
   // created on page load to reduce the amount of boilerplate code necessary.
   // However, doing so during AR tests currently fails due to AR sessions
@@ -201,15 +220,13 @@
   // inline session creation.
   if (typeof shouldAutoCreateNonImmersiveSession === 'undefined'
       || shouldAutoCreateNonImmersiveSession === true) {
-    navigator.xr.requestSession()
-        .then((session) => {
-          session.updateRenderState({
-            outputContext: ctx
-          });
-          onSessionStarted(session);
-        }).then( () => {
-          initializationSteps['magicWindowStarted'] = true;
-        });
+
+    // Separate if statement to keep the logic around setting initialization
+    // steps cleaner.
+    if (typeof shouldDeferNonImmersiveSessionCreation === 'undefined'
+      || shouldDeferNonImmersiveSessionCreation === false) {
+      requestMagicWindowSession();
+    }
   } else {
     initializationSteps['magicWindowStarted'] = true;
   }
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 4ffe6a7..4fffe1c 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -15,7 +15,10 @@
 
 buildflag_header("browser_buildflags") {
   header = "browser_buildflags.h"
-  flags = [ "ENABLE_CAST_AUDIO_MANAGER_MIXER=$enable_cast_audio_manager_mixer" ]
+  flags = [
+    "ENABLE_CAST_AUDIO_MANAGER_MIXER=$enable_cast_audio_manager_mixer",
+    "ENABLE_CAST_RENDERER=$enable_cast_renderer",
+  ]
 }
 
 cast_source_set("prefs") {
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 44f283e..a1efdca 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -150,10 +150,7 @@
 static void CreateMediaService(CastContentBrowserClient* browser_client,
                                service_manager::mojom::ServiceRequest request) {
   std::unique_ptr<::media::MediaService> service;
-#if defined(OS_ANDROID)
-  service = std::make_unique<::media::MediaService>(
-      std::make_unique<::media::AndroidMojoMediaClient>(), std::move(request));
-#else
+#if BUILDFLAG(ENABLE_CAST_RENDERER)
   auto mojo_media_client = std::make_unique<media::CastMojoMediaClient>(
       browser_client->GetCmaBackendFactory(),
       base::Bind(&CastContentBrowserClient::CreateCdmFactory,
@@ -163,8 +160,12 @@
       browser_client->media_resource_tracker());
   service = std::make_unique<::media::MediaService>(
       std::move(mojo_media_client), std::move(request));
-#endif  // defined(OS_ANDROID)
-
+#elif defined(OS_ANDROID)
+  service = std::make_unique<::media::MediaService>(
+      std::make_unique<::media::AndroidMojoMediaClient>(), std::move(request));
+#else
+#error "Unsupported configuration."
+#endif  // defined(ENABLE_CAST_RENDERER)
   service_manager::Service::RunAsyncUntilTermination(std::move(service));
 }
 #endif  // BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 2c0bbb31..838cb6ad 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-11921.0.0
\ No newline at end of file
+11945.0.0
\ No newline at end of file
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index d70bf09..87328b61 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -64,6 +64,12 @@
 const base::Feature kInstantTethering{"InstantTethering",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables the Material Design version of the settings Set Time dialog.
+// TODO: When this becomes the default the JS "i18n-options" attribute can be
+// deleted.
+const base::Feature kSetTimeDialogMd{"SetTimeDialogMd",
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enable or disable native controls in video player on Chrome OS.
 const base::Feature kVideoPlayerNativeControls{
     "VideoPlayerNativeControls", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -90,6 +96,10 @@
 const base::Feature kUserActivityPredictionMlService{
     "UserActivityPredictionMlService", base::FEATURE_DISABLED_BY_DEFAULT};
 
+bool IsSetTimeDialogMd() {
+  return base::FeatureList::IsEnabled(kSetTimeDialogMd);
+}
+
 }  // namespace features
 
 }  // namespace chromeos
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 09f0bc5..f5681bae 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -36,6 +36,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kInstantTethering;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kSetTimeDialogMd;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kVideoPlayerNativeControls;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kUseMessagesGoogleComDomain;
@@ -46,6 +48,9 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kUserActivityPredictionMlService;
 
+// Returns whether the settings Set Time dialog uses Material Design.
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsSetTimeDialogMd();
+
 }  // namespace features
 
 }  // namespace chromeos
diff --git a/chromeos/network/onc/variable_expander.cc b/chromeos/network/onc/variable_expander.cc
index fd72752c..cd5bbb2 100644
--- a/chromeos/network/onc/variable_expander.cc
+++ b/chromeos/network/onc/variable_expander.cc
@@ -145,6 +145,12 @@
       // Nothing to do here.
       break;
     }
+
+    // TODO(crbug.com/859477): Remove after root cause is found.
+    case base::Value::Type::DEAD: {
+      CHECK(false);
+      break;
+    }
   }
   return no_error;
 }
diff --git a/chromeos/services/assistant/public/features.cc b/chromeos/services/assistant/public/features.cc
index b48fb73..f777951 100644
--- a/chromeos/services/assistant/public/features.cc
+++ b/chromeos/services/assistant/public/features.cc
@@ -23,7 +23,7 @@
     "AssistantWarmerWelcome", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kAssistantAppSupport{"AssistantAppSupport",
-                                         base::FEATURE_DISABLED_BY_DEFAULT};
+                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kAssistantRoutines{"AssistantRoutines",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 28bae00..ce36560 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -126,8 +126,8 @@
     "form_types.h",
     "label_formatter.cc",
     "label_formatter.h",
-    "label_formatter_creator.cc",
-    "label_formatter_creator.h",
+    "label_formatter_utils.cc",
+    "label_formatter_utils.h",
     "metrics/address_form_event_logger.cc",
     "metrics/address_form_event_logger.h",
     "metrics/credit_card_form_event_logger.cc",
@@ -517,6 +517,7 @@
   sources = [
     "address_combobox_model_unittest.cc",
     "address_field_unittest.cc",
+    "address_form_label_formatter_unittest.cc",
     "address_i18n_unittest.cc",
     "address_normalization_manager_unittest.cc",
     "address_normalizer_impl_unittest.cc",
@@ -541,6 +542,7 @@
     "autofill_profile_validator_unittest.cc",
     "autofill_subject_unittest.cc",
     "autofill_type_unittest.cc",
+    "contact_form_label_formatter_unittest.cc",
     "contact_info_unittest.cc",
     "country_combobox_model_unittest.cc",
     "country_names_unittest.cc",
@@ -551,7 +553,8 @@
     "form_data_importer_unittest.cc",
     "form_field_unittest.cc",
     "form_structure_unittest.cc",
-    "label_formatter_creator_unittest.cc",
+    "label_formatter_unittest.cc",
+    "label_formatter_utils_unittest.cc",
     "name_field_unittest.cc",
     "password_generator_fips181_unittest.cc",
     "password_generator_unittest.cc",
diff --git a/components/autofill/core/browser/address_form_label_formatter.cc b/components/autofill/core/browser/address_form_label_formatter.cc
index 638bf629..9130868 100644
--- a/components/autofill/core/browser/address_form_label_formatter.cc
+++ b/components/autofill/core/browser/address_form_label_formatter.cc
@@ -4,6 +4,8 @@
 
 #include "components/autofill/core/browser/address_form_label_formatter.h"
 
+#include "components/autofill/core/browser/label_formatter_utils.h"
+
 namespace autofill {
 
 AddressFormLabelFormatter::AddressFormLabelFormatter(
@@ -16,8 +18,15 @@
 
 std::vector<base::string16> AddressFormLabelFormatter::GetLabels(
     const std::vector<AutofillProfile*>& profiles) const {
-  // TODO(crbug.com/936168): Implement GetLabels().
   std::vector<base::string16> labels;
+  for (const AutofillProfile* profile : profiles) {
+    if (focused_group() == ADDRESS_HOME) {
+      labels.push_back(GetLabelName(*profile, app_locale()));
+    } else {
+      labels.push_back(GetLabelNationalAddress(*profile, app_locale(),
+                                               field_types_for_labels()));
+    }
+  }
   return labels;
 }
 
diff --git a/components/autofill/core/browser/address_form_label_formatter_unittest.cc b/components/autofill/core/browser/address_form_label_formatter_unittest.cc
new file mode 100644
index 0000000..0dad8b2
--- /dev/null
+++ b/components/autofill/core/browser/address_form_label_formatter_unittest.cc
@@ -0,0 +1,175 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/address_form_label_formatter.h"
+
+#include <vector>
+
+#include "base/guid.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+
+namespace autofill {
+namespace {
+
+std::vector<ServerFieldType> GetFieldTypes() {
+  return {NAME_FIRST,
+          NAME_LAST,
+          ADDRESS_HOME_LINE1,
+          ADDRESS_HOME_LINE2,
+          ADDRESS_HOME_DEPENDENT_LOCALITY,
+          ADDRESS_HOME_CITY,
+          ADDRESS_HOME_STATE,
+          ADDRESS_HOME_ZIP,
+          ADDRESS_HOME_COUNTRY};
+}
+
+TEST(AddressFormLabelFormatterTest, GetLabelsWithMissingProfiles) {
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create("en-US", NAME_FIRST, GetFieldTypes());
+  EXPECT_TRUE(formatter->GetLabels(std::vector<AutofillProfile*>()).empty());
+}
+
+TEST(AddressFormLabelFormatterTest, GetLabelsForUSProfilesAndFocusedAddress) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
+                       "333 Washington St", "", "Brookline", "MA", "02445",
+                       "US", "16177302000");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "jackie@outlook.com",
+                       "", "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
+                       "5087717796");
+
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create("en-US", ADDRESS_HOME_LINE1, GetFieldTypes());
+
+  EXPECT_THAT(
+      formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2}),
+      ElementsAre(base::ASCIIToUTF16("John F Kennedy"),
+                  base::ASCIIToUTF16("Jackie Kennedy")));
+}
+
+TEST(AddressFormLabelFormatterTest,
+     GetLabelsForUSProfilesAndFocusedNonAddress) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
+                       "333 Washington St", "", "Brookline", "MA", "02445",
+                       "US", "16177302000");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "jackie@outlook.com",
+                       "", "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
+                       "5087717796");
+
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create("en-US", NAME_FIRST, GetFieldTypes());
+
+  EXPECT_THAT(
+      formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2}),
+      ElementsAre(base::ASCIIToUTF16("333 Washington St, Brookline, MA 02445"),
+                  base::ASCIIToUTF16("151 Irving Ave, Hyannis, MA 02601")));
+}
+
+TEST(AddressFormLabelFormatterTest, GetLabelsForBRProfilesAndFocusedAddress) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
+                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
+                       "São Paulo", "SP", "04094-050", "BR", "");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "", "",
+                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
+                       "Rio de Janeiro", "RJ", "22460-320", "BR", "");
+
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create("pt-BR", ADDRESS_HOME_LINE1, GetFieldTypes());
+
+  EXPECT_THAT(
+      formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2}),
+      ElementsAre(base::ASCIIToUTF16("Tarsila do Amaral"),
+                  base::ASCIIToUTF16("Artur Avila")));
+}
+
+TEST(AddressFormLabelFormatterTest,
+     GetLabelsForBRProfilesAndFocusedNonAddress) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
+                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
+                       "São Paulo", "SP", "04094-050", "BR", "");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "", "",
+                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
+                       "Rio de Janeiro", "RJ", "22460-320", "BR", "");
+
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create("pt-BR", NAME_FIRST, GetFieldTypes());
+
+  EXPECT_THAT(
+      formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2}),
+      ElementsAre(
+          base::UTF8ToUTF16("Av. Pedro Álvares Cabral, 1301, Vila Mariana, São "
+                            "Paulo-SP, 04094-050"),
+          base::UTF8ToUTF16(
+              "Estr. Dona Castorina, 110, Jardim Botânico, Rio de "
+              "Janeiro-RJ, 22460-320")));
+}
+
+TEST(AddressFormLabelFormatterTest,
+     GetLabelsForProfilesWithIncompleteNamesAndFocusedAddress) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "", "", "", "", "", "19 N Square", "",
+                       "Boston", "MA", "02113", "US", "");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "", "", "Adams", "", "", "141 Franklin St",
+                       "", "Quincy", "MA", "02169", "US", "");
+
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create("en-US", ADDRESS_HOME_LINE1, GetFieldTypes());
+
+  EXPECT_THAT(
+      formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2}),
+      ElementsAre(base::string16(), base::ASCIIToUTF16("Adams")));
+}
+
+TEST(AddressFormLabelFormatterTest,
+     GetLabelsForProfilesWithIncompleteAddressAndFocusedNonAddress) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Paul", "", "Revere", "paul1775@gmail.com",
+                       "", "", "", "", "", "", "", "");
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "John", "", "Adams", "", "",
+                       "141 Franklin St", "", "", "", "", "", "");
+
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create("en-US", NAME_FIRST, GetFieldTypes());
+
+  EXPECT_THAT(
+      formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2}),
+      ElementsAre(base::string16(), base::ASCIIToUTF16("141 Franklin St")));
+}
+
+}  // namespace
+}  // namespace autofill
\ No newline at end of file
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index dcd07497..ab993477 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -5395,9 +5395,8 @@
 // Verify that typing "gmail" will match "theking@gmail.com" and
 // "buddy@gmail.com" when substring matching is enabled.
 TEST_F(AutofillManagerTest, DisplaySuggestionsWithMatchingTokens) {
-  // Token matching is currently behind a flag.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableSuggestionsWithSubstringMatch);
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(features::kAutofillTokenPrefixMatching);
 
   // Set up our form data.
   FormData form;
@@ -5417,9 +5416,8 @@
 // Verify that typing "apple" will match "123 Apple St." when substring matching
 // is enabled.
 TEST_F(AutofillManagerTest, DisplaySuggestionsWithMatchingTokens_CaseIgnored) {
-  // Token matching is currently behind a flag.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableSuggestionsWithSubstringMatch);
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(features::kAutofillTokenPrefixMatching);
 
   // Set up our form data.
   FormData form;
@@ -5438,9 +5436,8 @@
 // Verify that typing "mail" will not match any of the "@gmail.com" email
 // addresses when substring matching is enabled.
 TEST_F(AutofillManagerTest, NoSuggestionForNonPrefixTokenMatch) {
-  // Token matching is currently behind a flag.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableSuggestionsWithSubstringMatch);
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(features::kAutofillTokenPrefixMatching);
 
   // Set up our form data.
   FormData form;
@@ -5457,9 +5454,8 @@
 // Verify that typing "pres" will match "Elvis Presley" when substring matching
 // is enabled.
 TEST_F(AutofillManagerTest, DisplayCreditCardSuggestionsWithMatchingTokens) {
-  // Token matching is currently behind a flag.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableSuggestionsWithSubstringMatch);
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(features::kAutofillTokenPrefixMatching);
 
   // Set up our form data.
   FormData form;
@@ -5488,9 +5484,8 @@
 // Verify that typing "lvis" will not match any of the credit card name when
 // substring matching is enabled.
 TEST_F(AutofillManagerTest, NoCreditCardSuggestionsForNonPrefixTokenMatch) {
-  // Token matching is currently behind a flag.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableSuggestionsWithSubstringMatch);
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(features::kAutofillTokenPrefixMatching);
 
   // Set up our form data.
   FormData form;
@@ -5698,9 +5693,8 @@
 // substring matched.
 TEST_F(AutofillManagerTest,
        DisplaySuggestionsWithPrefixesPrecedeSubstringMatched) {
-  // Token matching is currently behind a flag.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableSuggestionsWithSubstringMatch);
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(features::kAutofillTokenPrefixMatching);
 
   // Set up our form data.
   FormData form;
diff --git a/components/autofill/core/browser/contact_form_label_formatter.cc b/components/autofill/core/browser/contact_form_label_formatter.cc
index 8ce18e9..bfe00b3dc 100644
--- a/components/autofill/core/browser/contact_form_label_formatter.cc
+++ b/components/autofill/core/browser/contact_form_label_formatter.cc
@@ -4,21 +4,95 @@
 
 #include "components/autofill/core/browser/contact_form_label_formatter.h"
 
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/label_formatter_utils.h"
+#include "components/autofill/core/browser/phone_number_i18n.h"
+#include "components/autofill/core/browser/validation.h"
+
 namespace autofill {
 
 ContactFormLabelFormatter::ContactFormLabelFormatter(
     const std::string& app_locale,
     FieldTypeGroup focused_group,
+    uint32_t groups,
     const std::vector<ServerFieldType>& field_types)
-    : LabelFormatter(app_locale, focused_group, field_types) {}
+    : LabelFormatter(app_locale, focused_group, field_types), groups_(groups) {}
 
 ContactFormLabelFormatter::~ContactFormLabelFormatter() {}
 
 std::vector<base::string16> ContactFormLabelFormatter::GetLabels(
     const std::vector<AutofillProfile*>& profiles) const {
-  // TODO(crbug.com/936168): Implement GetLabels().
   std::vector<base::string16> labels;
+
+  for (const AutofillProfile* profile : profiles) {
+    switch (focused_group()) {
+      case EMAIL:
+        labels.push_back(GetLabelForFocusedEmail(*profile));
+        break;
+
+      case PHONE_HOME:
+        labels.push_back(GetLabelForFocusedPhone(*profile));
+        break;
+
+      default:
+        labels.push_back(GetLabelDefault(*profile));
+    }
+  }
   return labels;
 }
 
+void ContactFormLabelFormatter::MaybeAddEmail(
+    const AutofillProfile& profile,
+    std::vector<base::string16>* label_parts) const {
+  const base::string16 email = ContainsEmail(groups_)
+                                   ? GetLabelEmail(profile, app_locale())
+                                   : base::string16();
+  if (!email.empty() && IsValidEmailAddress(email)) {
+    label_parts->push_back(email);
+  }
+}
+
+void ContactFormLabelFormatter::MaybeAddPhone(
+    const AutofillProfile& profile,
+    std::vector<base::string16>* label_parts) const {
+  const base::string16 phone = ContainsPhone(groups_)
+                                   ? GetLabelPhone(profile, app_locale())
+                                   : base::string16();
+  if (!phone.empty()) {
+    label_parts->push_back(phone);
+  }
+}
+
+base::string16 ContactFormLabelFormatter::GetLabelForFocusedEmail(
+    const AutofillProfile& profile) const {
+  std::vector<base::string16> label_parts;
+  MaybeAddPhone(profile, &label_parts);
+
+  const base::string16 name = GetLabelName(profile, app_locale());
+  if (!name.empty()) {
+    label_parts.push_back(name);
+  }
+  return base::JoinString(label_parts, base::WideToUTF16(kLabelDelimiter));
+}
+
+base::string16 ContactFormLabelFormatter::GetLabelForFocusedPhone(
+    const AutofillProfile& profile) const {
+  std::vector<base::string16> label_parts;
+  const base::string16 name = GetLabelName(profile, app_locale());
+  if (!name.empty()) {
+    label_parts.push_back(name);
+  }
+  MaybeAddEmail(profile, &label_parts);
+  return base::JoinString(label_parts, base::WideToUTF16(kLabelDelimiter));
+}
+
+base::string16 ContactFormLabelFormatter::GetLabelDefault(
+    const AutofillProfile& profile) const {
+  std::vector<base::string16> label_parts;
+  MaybeAddPhone(profile, &label_parts);
+  MaybeAddEmail(profile, &label_parts);
+  return base::JoinString(label_parts, base::WideToUTF16(kLabelDelimiter));
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/contact_form_label_formatter.h b/components/autofill/core/browser/contact_form_label_formatter.h
index 1de7cfe..834cd82 100644
--- a/components/autofill/core/browser/contact_form_label_formatter.h
+++ b/components/autofill/core/browser/contact_form_label_formatter.h
@@ -22,12 +22,40 @@
  public:
   ContactFormLabelFormatter(const std::string& app_locale,
                             FieldTypeGroup focused_group,
+                            uint32_t groups,
                             const std::vector<ServerFieldType>& field_types);
 
   ~ContactFormLabelFormatter() override;
 
   std::vector<base::string16> GetLabels(
       const std::vector<AutofillProfile*>& profiles) const override;
+
+ private:
+  // Adds |profile|'s email address to |label_parts| if |profile| has a valid
+  // email address and if this formatter's associated form has an email field.
+  void MaybeAddEmail(const AutofillProfile& profile,
+                     std::vector<base::string16>* label_parts) const;
+
+  // Adds |profile|'s phone number to |label_parts| if |profile| has a phone
+  // number and if this formatter's associated form has a phone field.
+  void MaybeAddPhone(const AutofillProfile& profile,
+                     std::vector<base::string16>* label_parts) const;
+
+  // Returns a label to show the user when the focused field is related to
+  // phone numbers.
+  base::string16 GetLabelForFocusedPhone(const AutofillProfile& profile) const;
+
+  // Returns a label to show the user when the focused field is related to
+  // email addresses.
+  base::string16 GetLabelForFocusedEmail(const AutofillProfile& profile) const;
+
+  // Returns a label to show the user when the focused field is related to
+  // neither phone numbers nor addresses. This is used, for example, when the
+  // user focuses on a name-related field.
+  base::string16 GetLabelDefault(const AutofillProfile& profile) const;
+
+  // A bitmask indicating which fields the form contains.
+  uint32_t groups_;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/contact_form_label_formatter_unittest.cc b/components/autofill/core/browser/contact_form_label_formatter_unittest.cc
new file mode 100644
index 0000000..b16f3f0
--- /dev/null
+++ b/components/autofill/core/browser/contact_form_label_formatter_unittest.cc
@@ -0,0 +1,291 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/contact_form_label_formatter.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/guid.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/label_formatter_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+
+namespace autofill {
+namespace {
+
+std::vector<ServerFieldType> GetNamePhoneAndEmailFieldTypes() {
+  return {NAME_FIRST, NAME_LAST, PHONE_HOME_WHOLE_NUMBER, EMAIL_ADDRESS};
+}
+
+base::string16 FormatExpectedLabel(base::StringPiece label_part1,
+                                   base::StringPiece label_part2) {
+  return base::JoinString(
+      {base::ASCIIToUTF16(label_part1), base::ASCIIToUTF16(label_part2)},
+      base::WideToUTF16(kLabelDelimiter));
+}
+
+TEST(ContactFormLabelFormatterTest, GetLabelsWithMissingProfiles) {
+  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
+      "en-US", NAME_FIRST, GetNamePhoneAndEmailFieldTypes());
+  EXPECT_TRUE(formatter->GetLabels(std::vector<AutofillProfile*>()).empty());
+}
+
+TEST(ContactFormLabelFormatterTest,
+     GetLabelsForUSProfilesAndFocusedNonEmailNonPhone) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
+                       "333 Washington St", "", "Brookline", "MA", "02445",
+                       "US", "16177302000");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "jackie@outlook.com",
+                       "", "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
+                       "");
+
+  AutofillProfile profile3 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "", "", "19 N Square",
+                       "", "Boston", "MA", "02113", "US", "+1 (617) 523-2338");
+
+  AutofillProfile profile4 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile4, "John", "", "Adams", "", "",
+                       "141 Franklin St.", "", "Quincy", "MA", "02169", "US",
+                       "");
+
+  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
+      "en-US", NAME_LAST, GetNamePhoneAndEmailFieldTypes());
+
+  EXPECT_THAT(
+      formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2,
+                                                         &profile3, &profile4}),
+      ElementsAre(FormatExpectedLabel("(617) 730-2000", "jfk@gmail.com"),
+                  base::ASCIIToUTF16("jackie@outlook.com"),
+                  base::ASCIIToUTF16("(617) 523-2338"), base::string16()));
+}
+
+TEST(ContactFormLabelFormatterTest, GetLabelsForUSProfilesAndFocusedEmail) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
+                       "333 Washington St", "", "Brookline", "MA", "02445",
+                       "US", "16177302000");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "jackie@outlook.com",
+                       "", "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
+                       "");
+
+  AutofillProfile profile3 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "", "", "19 N Square",
+                       "", "Boston", "MA", "02113", "US", "+1 (617) 523-2338");
+
+  AutofillProfile profile4 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile4, "", "", "", "", "", "141 Franklin St.", "",
+                       "Quincy", "MA", "02169", "US", "");
+
+  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
+      "en-US", EMAIL_ADDRESS, GetNamePhoneAndEmailFieldTypes());
+
+  EXPECT_THAT(
+      formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2,
+                                                         &profile3, &profile4}),
+      ElementsAre(FormatExpectedLabel("(617) 730-2000", "John F Kennedy"),
+                  base::ASCIIToUTF16("Jackie Kennedy"),
+                  FormatExpectedLabel("(617) 523-2338", "Paul Revere"),
+                  base::string16()));
+}
+
+TEST(ContactFormLabelFormatterTest, GetLabelsForUSProfilesAndFocusedPhone) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
+                       "333 Washington St", "", "Brookline", "MA", "02445",
+                       "US", "16177302000");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "jackie@outlook.com",
+                       "", "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
+                       "");
+
+  AutofillProfile profile3 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "", "", "19 N Square",
+                       "", "Boston", "MA", "02113", "US", "+1 (617) 523-2338");
+
+  AutofillProfile profile4 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile4, "", "", "", "", "", "141 Franklin St.", "",
+                       "Quincy", "MA", "02169", "US", "");
+
+  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
+      "en-US", PHONE_HOME_WHOLE_NUMBER, GetNamePhoneAndEmailFieldTypes());
+
+  EXPECT_THAT(
+      formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2,
+                                                         &profile3, &profile4}),
+      ElementsAre(FormatExpectedLabel("John F Kennedy", "jfk@gmail.com"),
+                  FormatExpectedLabel("Jackie Kennedy", "jackie@outlook.com"),
+                  base::ASCIIToUTF16("Paul Revere"), base::string16()));
+}
+
+TEST(ContactFormLabelFormatterTest,
+     GetLabelsForBRProfilesAndFocusedNonEmailNonPhone) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
+                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
+                       "São Paulo", "SP", "04094-050", "BR",
+                       "+55 11 2648-0254");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
+                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
+                       "Rio de Janeiro", "RJ", "22460-320", "BR",
+                       "21987650000");
+
+  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
+      "pt-BR", NAME_LAST, GetNamePhoneAndEmailFieldTypes());
+
+  EXPECT_THAT(
+      formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2}),
+      ElementsAre(FormatExpectedLabel("(11) 2648-0254", "tarsila@aol.com"),
+                  FormatExpectedLabel("(21) 98765-0000", "aavila@uol.com.br")));
+}
+
+TEST(ContactFormLabelFormatterTest, GetLabelsForBRProfilesAndFocusedEmail) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
+                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
+                       "São Paulo", "SP", "04094-050", "BR",
+                       "+55 11 2648-0254");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
+                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
+                       "Rio de Janeiro", "RJ", "22460-320", "BR",
+                       "21987650000");
+
+  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
+      "pt-BR", EMAIL_ADDRESS, GetNamePhoneAndEmailFieldTypes());
+
+  EXPECT_THAT(
+      formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2}),
+      ElementsAre(FormatExpectedLabel("(11) 2648-0254", "Tarsila do Amaral"),
+                  FormatExpectedLabel("(21) 98765-0000", "Artur Avila")));
+}
+
+TEST(ContactFormLabelFormatterTest, GetLabelsForBRProfilesAndFocusedPhone) {
+  AutofillProfile profile1 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
+                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
+                       "São Paulo", "SP", "04094-050", "BR",
+                       "+55 11 2648-0254");
+
+  AutofillProfile profile2 =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
+                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
+                       "Rio de Janeiro", "RJ", "22460-320", "BR",
+                       "21987650000");
+
+  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
+      "pt-BR", PHONE_HOME_WHOLE_NUMBER, GetNamePhoneAndEmailFieldTypes());
+
+  EXPECT_THAT(
+      formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2}),
+      ElementsAre(FormatExpectedLabel("Tarsila do Amaral", "tarsila@aol.com"),
+                  FormatExpectedLabel("Artur Avila", "aavila@uol.com.br")));
+}
+
+TEST(ContactFormLabelFormatterTest,
+     GetLabelsForNameAndPhoneWithFocusedNonPhone) {
+  AutofillProfile profile =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile, "John", "F", "Kennedy", "jfk@gmail.com", "",
+                       "333 Washington St", "", "Brookline", "MA", "02445",
+                       "US", "16177302000");
+
+  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
+      "en-US", NAME_LAST, {NAME_FIRST, NAME_LAST, PHONE_HOME_WHOLE_NUMBER});
+
+  // Checks that the email address is excluded when the form does not contain an
+  // email field.
+  EXPECT_THAT(formatter->GetLabels(std::vector<AutofillProfile*>{&profile}),
+              ElementsAre(base::ASCIIToUTF16("(617) 730-2000")));
+}
+
+TEST(ContactFormLabelFormatterTest, GetLabelsForNameAndPhoneWithFocusedPhone) {
+  AutofillProfile profile =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile, "John", "F", "Kennedy", "jfk@gmail.com", "",
+                       "333 Washington St", "", "Brookline", "MA", "02445",
+                       "US", "16177302000");
+
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create("en-US", PHONE_HOME_WHOLE_NUMBER,
+                             {NAME_FIRST, NAME_LAST, PHONE_HOME_WHOLE_NUMBER});
+
+  // Checks that the email address is excluded when the form does not contain an
+  // email field.
+  EXPECT_THAT(formatter->GetLabels(std::vector<AutofillProfile*>{&profile}),
+              ElementsAre(base::ASCIIToUTF16("John F Kennedy")));
+}
+
+TEST(ContactFormLabelFormatterTest,
+     GetLabelsForNameAndEmailWithFocusedNonEmail) {
+  AutofillProfile profile =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile, "John", "F", "Kennedy", "jfk@gmail.com", "",
+                       "333 Washington St", "", "Brookline", "MA", "02445",
+                       "US", "16177302000");
+
+  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
+      "en-US", NAME_LAST, {NAME_FIRST, NAME_LAST, EMAIL_ADDRESS});
+
+  // Checks that the phone number is excluded when the form does not contain a
+  // phone field.
+  EXPECT_THAT(formatter->GetLabels(std::vector<AutofillProfile*>{&profile}),
+              ElementsAre(base::ASCIIToUTF16("jfk@gmail.com")));
+}
+
+TEST(ContactFormLabelFormatterTest, GetLabelsForNameAndEmailWithFocusedEmail) {
+  AutofillProfile profile =
+      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
+  test::SetProfileInfo(&profile, "John", "F", "Kennedy", "jfk@gmail.com", "",
+                       "333 Washington St", "", "Brookline", "MA", "02445",
+                       "US", "16177302000");
+
+  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
+      "en-US", EMAIL_ADDRESS, {NAME_FIRST, NAME_LAST, EMAIL_ADDRESS});
+
+  // Checks that the phone number is excluded when the form does not contain a
+  // phone field.
+  EXPECT_THAT(formatter->GetLabels(std::vector<AutofillProfile*>{&profile}),
+              ElementsAre(base::ASCIIToUTF16("John F Kennedy")));
+}
+
+}  // namespace
+}  // namespace autofill
\ No newline at end of file
diff --git a/components/autofill/core/browser/label_formatter.cc b/components/autofill/core/browser/label_formatter.cc
index b3e3c61..b7163fa 100644
--- a/components/autofill/core/browser/label_formatter.cc
+++ b/components/autofill/core/browser/label_formatter.cc
@@ -8,8 +8,20 @@
 #include <iterator>
 #include <set>
 
+#include "components/autofill/core/browser/address_contact_form_label_formatter.h"
+#include "components/autofill/core/browser/address_email_form_label_formatter.h"
+#include "components/autofill/core/browser/address_form_label_formatter.h"
+#include "components/autofill/core/browser/address_phone_form_label_formatter.h"
+#include "components/autofill/core/browser/contact_form_label_formatter.h"
+#include "components/autofill/core/browser/label_formatter_utils.h"
+
 namespace autofill {
 
+using label_formatter_groups::kAddress;
+using label_formatter_groups::kEmail;
+using label_formatter_groups::kName;
+using label_formatter_groups::kPhone;
+
 LabelFormatter::LabelFormatter(const std::string& app_locale,
                                FieldTypeGroup focused_group,
                                const std::vector<ServerFieldType>& field_types)
@@ -23,11 +35,45 @@
                groups.end() &&
            type != ADDRESS_HOME_COUNTRY && type != ADDRESS_BILLING_COUNTRY;
   };
-
   std::copy_if(field_types.begin(), field_types.end(),
                back_inserter(field_types_for_labels_), can_be_shown_in_label);
 }
 
 LabelFormatter::~LabelFormatter() = default;
 
+// static
+std::unique_ptr<LabelFormatter> LabelFormatter::Create(
+    const std::string& app_locale,
+    ServerFieldType focused_field_type,
+    const std::vector<ServerFieldType>& field_types) {
+  const uint32_t groups = DetermineGroups(field_types);
+  if (!ContainsName(groups)) {
+    return nullptr;
+  }
+
+  const FieldTypeGroup focused_group =
+      AutofillType(AutofillType(focused_field_type).GetStorableType()).group();
+  switch (groups) {
+    case kName | kAddress | kEmail | kPhone:
+      return std::make_unique<AddressContactFormLabelFormatter>(
+          app_locale, focused_group, field_types);
+    case kName | kAddress | kPhone:
+      return std::make_unique<AddressPhoneFormLabelFormatter>(
+          app_locale, focused_group, field_types);
+    case kName | kAddress | kEmail:
+      return std::make_unique<AddressEmailFormLabelFormatter>(
+          app_locale, focused_group, field_types);
+    case kName | kAddress:
+      return std::make_unique<AddressFormLabelFormatter>(
+          app_locale, focused_group, field_types);
+    case kName | kEmail | kPhone:
+    case kName | kEmail:
+    case kName | kPhone:
+      return std::make_unique<ContactFormLabelFormatter>(
+          app_locale, focused_group, groups, field_types);
+    default:
+      return nullptr;
+  }
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/label_formatter.h b/components/autofill/core/browser/label_formatter.h
index 9d74e15..203eab4d 100644
--- a/components/autofill/core/browser/label_formatter.h
+++ b/components/autofill/core/browser/label_formatter.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_LABEL_FORMATTER_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_LABEL_FORMATTER_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -27,6 +28,14 @@
   virtual std::vector<base::string16> GetLabels(
       const std::vector<AutofillProfile*>& profiles) const = 0;
 
+  // Creates a form-specific LabelFormatter according to |field_types|. If the
+  // given |field_types| do not correspond to a LabelFormatter, then nullptr
+  // will be returned.
+  static std::unique_ptr<LabelFormatter> Create(
+      const std::string& app_locale,
+      ServerFieldType focused_field_type,
+      const std::vector<ServerFieldType>& field_types);
+
  protected:
   const std::string& app_locale() const { return app_locale_; }
   FieldTypeGroup focused_group() const { return focused_group_; }
diff --git a/components/autofill/core/browser/label_formatter_creator.cc b/components/autofill/core/browser/label_formatter_creator.cc
deleted file mode 100644
index d1e8dd6..0000000
--- a/components/autofill/core/browser/label_formatter_creator.cc
+++ /dev/null
@@ -1,94 +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 "components/autofill/core/browser/label_formatter_creator.h"
-
-#include <memory>
-
-#include "components/autofill/core/browser/address_contact_form_label_formatter.h"
-#include "components/autofill/core/browser/address_email_form_label_formatter.h"
-#include "components/autofill/core/browser/address_form_label_formatter.h"
-#include "components/autofill/core/browser/address_phone_form_label_formatter.h"
-#include "components/autofill/core/browser/contact_form_label_formatter.h"
-
-namespace autofill {
-namespace {
-
-bool ContainsName(uint32_t groups) {
-  return groups & label_formatter_groups::kName;
-}
-
-bool ContainsAddress(uint32_t groups) {
-  return groups & label_formatter_groups::kAddress;
-}
-
-bool ContainsEmail(uint32_t groups) {
-  return groups & label_formatter_groups::kEmail;
-}
-
-bool ContainsPhone(uint32_t groups) {
-  return groups & label_formatter_groups::kPhone;
-}
-
-}  // namespace
-
-uint32_t DetermineGroups(const std::vector<ServerFieldType>& field_types) {
-  uint32_t group_bitmask = 0;
-  for (const ServerFieldType& type : field_types) {
-    const FieldTypeGroup group =
-        AutofillType(AutofillType(type).GetStorableType()).group();
-    switch (group) {
-      case autofill::NAME:
-        group_bitmask |= label_formatter_groups::kName;
-        break;
-      case autofill::ADDRESS_HOME:
-        group_bitmask |= label_formatter_groups::kAddress;
-        break;
-      case autofill::EMAIL:
-        group_bitmask |= label_formatter_groups::kEmail;
-        break;
-      case autofill::PHONE_HOME:
-        group_bitmask |= label_formatter_groups::kPhone;
-        break;
-      default:
-        break;
-    }
-  }
-  return group_bitmask;
-}
-
-std::unique_ptr<LabelFormatter> Create(
-    const std::string& app_locale,
-    ServerFieldType focused_field_type,
-    const std::vector<ServerFieldType>& field_types) {
-  const uint32_t groups = DetermineGroups(field_types);
-
-  if (!ContainsName(groups)) {
-    return nullptr;
-  }
-  const FieldTypeGroup focused_group =
-      AutofillType(AutofillType(focused_field_type).GetStorableType()).group();
-
-  if (ContainsAddress(groups) && ContainsEmail(groups) &&
-      ContainsPhone(groups)) {
-    return std::make_unique<AddressContactFormLabelFormatter>(
-        app_locale, focused_group, field_types);
-  } else if (ContainsAddress(groups) && ContainsPhone(groups)) {
-    return std::make_unique<AddressPhoneFormLabelFormatter>(
-        app_locale, focused_group, field_types);
-  } else if (ContainsAddress(groups) && ContainsEmail(groups)) {
-    return std::make_unique<AddressEmailFormLabelFormatter>(
-        app_locale, focused_group, field_types);
-  } else if (ContainsAddress(groups)) {
-    return std::make_unique<AddressFormLabelFormatter>(
-        app_locale, focused_group, field_types);
-  } else if (ContainsEmail(groups) || ContainsPhone(groups)) {
-    return std::make_unique<ContactFormLabelFormatter>(
-        app_locale, focused_group, field_types);
-  } else {
-    return nullptr;
-  }
-}
-
-}  // namespace autofill
diff --git a/components/autofill/core/browser/label_formatter_creator.h b/components/autofill/core/browser/label_formatter_creator.h
deleted file mode 100644
index 41dc8a44..0000000
--- a/components/autofill/core/browser/label_formatter_creator.h
+++ /dev/null
@@ -1,49 +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 COMPONENTS_AUTOFILL_CORE_BROWSER_LABEL_FORMATTER_CREATOR_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_LABEL_FORMATTER_CREATOR_H_
-
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/label_formatter.h"
-
-namespace autofill {
-namespace label_formatter_groups {
-
-// Bits for FieldTypeGroup options.
-// The form contains at least one field associated with the NAME_HOME or
-// NAME_BILLING FieldTypeGroups.
-constexpr uint32_t kName = 1 << 0;
-// The form contains at least one field associated with the ADDRESS_HOME or
-// ADDRESS_BILLING FieldTypeGroups.
-constexpr uint32_t kAddress = 1 << 1;
-// The form contains at least one field associated with the EMAIL
-// FieldTypeGroup.
-constexpr uint32_t kEmail = 1 << 2;
-// The form contains at least one field associated with the PHONE_HOME or
-// PHONE_BILLING FieldTypeGroup.
-constexpr uint32_t kPhone = 1 << 3;
-
-}  // namespace label_formatter_groups
-
-// Returns a bitmask indicating whether the NAME, ADDRESS_HOME, EMAIL, and
-// PHONE_HOME FieldTypeGroups are associated with the given |field_types|.
-uint32_t DetermineGroups(const std::vector<ServerFieldType>& field_types);
-
-// Creates a form-specific LabelFormatter according to |field_types|. If the
-// given |field_types| do not correspond to a LabelFormatter, then nullptr will
-// be returned.
-std::unique_ptr<LabelFormatter> Create(
-    const std::string& app_locale,
-    ServerFieldType focused_field_type,
-    const std::vector<ServerFieldType>& field_types);
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_LABEL_FORMATTER_CREATOR_H_
diff --git a/components/autofill/core/browser/label_formatter_unittest.cc b/components/autofill/core/browser/label_formatter_unittest.cc
new file mode 100644
index 0000000..df44216
--- /dev/null
+++ b/components/autofill/core/browser/label_formatter_unittest.cc
@@ -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.
+
+#include "components/autofill/core/browser/label_formatter.h"
+
+#include <vector>
+
+#include "components/autofill/core/browser/field_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+namespace {
+
+TEST(LabelFormatterTest, CreateWithMissingFieldTypes) {
+  EXPECT_EQ(LabelFormatter::Create("en-US", NAME_FIRST,
+                                   std::vector<ServerFieldType>()),
+            nullptr);
+}
+
+}  // namespace
+}  // namespace autofill
\ No newline at end of file
diff --git a/components/autofill/core/browser/label_formatter_utils.cc b/components/autofill/core/browser/label_formatter_utils.cc
new file mode 100644
index 0000000..b1bcfa4b
--- /dev/null
+++ b/components/autofill/core/browser/label_formatter_utils.cc
@@ -0,0 +1,112 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/label_formatter_utils.h"
+
+#include <memory>
+
+#include "components/autofill/core/browser/address_i18n.h"
+#include "components/autofill/core/browser/autofill_data_util.h"
+#include "components/autofill/core/browser/phone_number_i18n.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_formatter.h"
+
+namespace autofill {
+
+bool ContainsName(uint32_t groups) {
+  return groups & label_formatter_groups::kName;
+}
+
+bool ContainsAddress(uint32_t groups) {
+  return groups & label_formatter_groups::kAddress;
+}
+
+bool ContainsEmail(uint32_t groups) {
+  return groups & label_formatter_groups::kEmail;
+}
+
+bool ContainsPhone(uint32_t groups) {
+  return groups & label_formatter_groups::kPhone;
+}
+
+uint32_t DetermineGroups(const std::vector<ServerFieldType>& field_types) {
+  uint32_t group_bitmask = 0;
+  for (const ServerFieldType& type : field_types) {
+    const FieldTypeGroup group =
+        AutofillType(AutofillType(type).GetStorableType()).group();
+    switch (group) {
+      case autofill::NAME:
+        group_bitmask |= label_formatter_groups::kName;
+        break;
+      case autofill::ADDRESS_HOME:
+        group_bitmask |= label_formatter_groups::kAddress;
+        break;
+      case autofill::EMAIL:
+        group_bitmask |= label_formatter_groups::kEmail;
+        break;
+      case autofill::PHONE_HOME:
+        group_bitmask |= label_formatter_groups::kPhone;
+        break;
+      default:
+        break;
+    }
+  }
+  return group_bitmask;
+}
+
+AutofillProfile MakeTrimmedProfile(
+    const AutofillProfile& profile,
+    const std::string& app_locale,
+    const std::vector<ServerFieldType>& field_types) {
+  AutofillProfile trimmed_profile(profile.guid(), profile.origin());
+  trimmed_profile.set_language_code(profile.language_code());
+
+  const AutofillType country_code_type(HTML_TYPE_COUNTRY_CODE, HTML_MODE_NONE);
+  const base::string16 country_code =
+      profile.GetInfo(country_code_type, app_locale);
+  trimmed_profile.SetInfo(country_code_type, country_code, app_locale);
+
+  for (const ServerFieldType& type : field_types) {
+    trimmed_profile.SetInfo(type, profile.GetInfo(type, app_locale),
+                            app_locale);
+  }
+  return trimmed_profile;
+}
+
+base::string16 GetLabelName(const AutofillProfile& profile,
+                            const std::string& app_locale) {
+  return profile.GetInfo(AutofillType(NAME_FULL), app_locale);
+}
+
+base::string16 GetLabelNationalAddress(
+    const AutofillProfile& profile,
+    const std::string& app_locale,
+    const std::vector<ServerFieldType>& field_types) {
+  std::unique_ptr<::i18n::addressinput::AddressData> address_data =
+      i18n::CreateAddressDataFromAutofillProfile(
+          MakeTrimmedProfile(profile, app_locale, field_types), app_locale);
+
+  std::string address_line;
+  ::i18n::addressinput::GetFormattedNationalAddressLine(*address_data,
+                                                        &address_line);
+  return base::UTF8ToUTF16(address_line);
+}
+
+base::string16 GetLabelEmail(const AutofillProfile& profile,
+                             const std::string& app_locale) {
+  return profile.GetInfo(AutofillType(EMAIL_ADDRESS), app_locale);
+}
+
+base::string16 GetLabelPhone(const AutofillProfile& profile,
+                             const std::string& app_locale) {
+  const std::string unformatted_phone = base::UTF16ToUTF8(
+      profile.GetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER), app_locale));
+  return unformatted_phone.empty()
+             ? base::string16()
+             : base::UTF8ToUTF16(i18n::FormatPhoneNationallyForDisplay(
+                   unformatted_phone,
+                   data_util::GetCountryCodeWithFallback(profile, app_locale)));
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/label_formatter_utils.h b/components/autofill/core/browser/label_formatter_utils.h
new file mode 100644
index 0000000..06dfb794
--- /dev/null
+++ b/components/autofill/core/browser/label_formatter_utils.h
@@ -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.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_LABEL_FORMATTER_UTILS_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_LABEL_FORMATTER_UTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/field_types.h"
+
+namespace autofill {
+namespace label_formatter_groups {
+
+// Bits for FieldTypeGroup options.
+// The form contains at least one field associated with the NAME_HOME or
+// NAME_BILLING FieldTypeGroups.
+constexpr uint32_t kName = 1 << 0;
+// The form contains at least one field associated with the ADDRESS_HOME or
+// ADDRESS_BILLING FieldTypeGroups.
+constexpr uint32_t kAddress = 1 << 1;
+// The form contains at least one field associated with the EMAIL
+// FieldTypeGroup.
+constexpr uint32_t kEmail = 1 << 2;
+// The form contains at least one field associated with the PHONE_HOME or
+// PHONE_BILLING FieldTypeGroup.
+constexpr uint32_t kPhone = 1 << 3;
+
+}  // namespace label_formatter_groups
+
+// Used to separate the parts of a label that should appear on the same line,
+// for example, (202) 456-1111 • george.washington@gmail.com.
+constexpr wchar_t kLabelDelimiter[] = L" • ";
+
+// Returns true if kName is set in |groups|.
+bool ContainsName(uint32_t groups);
+
+// Returns true if kAddress is set in |groups|.
+bool ContainsAddress(uint32_t groups);
+
+// Returns true if kEmail is set in |groups|.
+bool ContainsEmail(uint32_t groups);
+
+// Returns true if kPhone is set in |groups|.
+bool ContainsPhone(uint32_t groups);
+
+// Returns a bitmask indicating whether the NAME, ADDRESS_HOME, EMAIL, and
+// PHONE_HOME FieldTypeGroups are associated with the given |field_types|.
+uint32_t DetermineGroups(const std::vector<ServerFieldType>& field_types);
+
+// Returns a pared down copy of |profile|. The copy has the same guid, origin,
+// country and language codes, and |field_types| as |profile|.
+AutofillProfile MakeTrimmedProfile(
+    const AutofillProfile& profile,
+    const std::string& app_locale,
+    const std::vector<ServerFieldType>& field_types);
+
+// Returns the full name associated with |profile|.
+base::string16 GetLabelName(const AutofillProfile& profile,
+                            const std::string& app_locale);
+
+// Returns the national address associated with |profile|, e.g.
+// 24 Beacon St., Boston, MA 02133.
+base::string16 GetLabelNationalAddress(
+    const AutofillProfile& profile,
+    const std::string& app_locale,
+    const std::vector<ServerFieldType>& field_types);
+
+// Returns the email address associated with |profile|, if any; otherwise,
+// returns an empty string.
+base::string16 GetLabelEmail(const AutofillProfile& profile,
+                             const std::string& app_locale);
+
+// Returns the phone number associated with |profile|, if any; otherwise,
+// returns an empty string. Phone numbers are given in |profile|'s country's
+// national format, if possible.
+base::string16 GetLabelPhone(const AutofillProfile& profile,
+                             const std::string& app_locale);
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_LABEL_FORMATTER_UTILS_H_
diff --git a/components/autofill/core/browser/label_formatter_creator_unittest.cc b/components/autofill/core/browser/label_formatter_utils_unittest.cc
similarity index 97%
rename from components/autofill/core/browser/label_formatter_creator_unittest.cc
rename to components/autofill/core/browser/label_formatter_utils_unittest.cc
index 46d8e402..833ccbb 100644
--- a/components/autofill/core/browser/label_formatter_creator_unittest.cc
+++ b/components/autofill/core/browser/label_formatter_utils_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 "components/autofill/core/browser/label_formatter_creator.h"
+#include "components/autofill/core/browser/label_formatter_utils.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/components/autofill/core/browser/suggestion_selection.cc b/components/autofill/core/browser/suggestion_selection.cc
index c244831..1a3f74b8 100644
--- a/components/autofill/core/browser/suggestion_selection.cc
+++ b/components/autofill/core/browser/suggestion_selection.cc
@@ -10,6 +10,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "components/autofill/core/browser/address_i18n.h"
+#include "components/autofill/core/browser/autofill_data_util.h"
 #include "components/autofill/core/browser/autofill_metrics.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/autofill_profile_comparator.h"
@@ -97,6 +98,21 @@
             suggestion_canon, field_contents_canon, type,
             /* is_masked_server_card= */ false, &prefix_matched_suggestion)) {
       matched_profiles->push_back(profile);
+
+      // If the field with which the user is interacting is a phone number or
+      // part of a phone number, then display it in the national format
+      // corresponding to the profile's country. For example, (508) 488-0800
+      // will be shown rather than 15084880800, 508 488 0800, or +15084880800
+      // for US phone numbers.
+      if (base::FeatureList::IsEnabled(
+              autofill::features::kAutofillShowFullDisclosureLabel) &&
+          AutofillType(AutofillType(server_field_type).GetStorableType())
+                  .group() == PHONE_HOME) {
+        value = base::UTF8ToUTF16(i18n::FormatPhoneNationallyForDisplay(
+            base::UTF16ToUTF8(value), data_util::GetCountryCodeWithFallback(
+                                          *profile, comparator.app_locale())));
+      }
+
       suggestions.push_back(Suggestion(value));
       suggestions.back().backend_id = profile->guid();
       suggestions.back().match = prefix_matched_suggestion
diff --git a/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
index 13d04b9..5ca2d3bd 100644
--- a/components/autofill/core/browser/webdata/autofill_table_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -2797,9 +2797,8 @@
 };
 
 TEST_P(GetFormValuesTest, GetFormValuesForElementName_SubstringMatchEnabled) {
-  // Token matching is currently behind a flag.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableSuggestionsWithSubstringMatch);
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(features::kAutofillTokenPrefixMatching);
 
   auto test_case = GetParam();
   SCOPED_TRACE(testing::Message()
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
index 60d37de6..95e10d8 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
@@ -423,6 +423,13 @@
     if (is_any_local_modified) {
       web_data_backend_->NotifyOfMultipleAutofillChanges();
     }
+
+    // Commit the transaction to make sure the sync data (deleted here) and the
+    // sync metadata and the progress marker (deleted by the processor via
+    // |delete_metadata_change_list|) get wiped from the DB. This is especially
+    // important on Android where we cannot rely on committing transactions on
+    // shutdown).
+    web_data_backend_->CommitChanges();
   }
 }
 
@@ -532,6 +539,10 @@
     }
   }
 
+  if (old_orphan_keys.empty()) {
+    return;
+  }
+
   std::unique_ptr<MetadataChangeList> metadata_change_list =
       CreateMetadataChangeList();
   for (const std::string storage_key : old_orphan_keys) {
@@ -543,6 +554,10 @@
       change_processor()->Delete(storage_key, metadata_change_list.get());
     }
   }
+  // Commit the transaction to make sure the data and the metadata is written
+  // down (especially on Android where we cannot rely on committing transactions
+  // on shutdown).
+  web_data_backend_->CommitChanges();
 }
 
 void AutofillWalletMetadataSyncBridge::GetDataImpl(
@@ -648,6 +663,13 @@
     }
   }
 
+  // Commit the transaction to make sure the data and the metadata with the
+  // new progress marker is written down (especially on Android where we
+  // cannot rely on committing transactions on shutdown). We need to commit
+  // even if !|is_any_local_modified| because the model type state or local
+  // metadata may have changed.
+  web_data_backend_->CommitChanges();
+
   if (is_any_local_modified) {
     web_data_backend_->NotifyOfMultipleAutofillChanges();
   }
@@ -710,6 +732,11 @@
           metadata_change_list.get());
       return;
   }
+
+  // We do not need to commit any local changes (written by the processor via
+  // the metadata change list) because the open WebDatabase transaction is
+  // committed by the AutofillWebDataService when the original local write
+  // operation (that triggered this notification to the bridge) finishes.
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
index d58556f..b99970e 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
@@ -583,6 +583,7 @@
   StartSyncing({profile, card});
 
   // Now stop sync. This should wipe the data and notify the backend.
+  EXPECT_CALL(*backend(), CommitChanges());
   EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
   StopSyncing();
 
@@ -603,6 +604,7 @@
 
   // Now simulate shutting down the browser. This should not touch any of the
   // data and thus also not notify the backend.
+  EXPECT_CALL(*backend(), CommitChanges()).Times(0);
   EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
   Shutdown();
 
@@ -627,6 +629,9 @@
       kCard1ServerId, /*use_count=*/2, /*use_date=*/5);
 
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+  // Local changes should not cause local DB writes.
+  EXPECT_CALL(*backend(), CommitChanges()).Times(0);
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
 
   bridge()->AutofillProfileChanged(
       AutofillProfileChange(AutofillProfileChange::UPDATE,
@@ -662,6 +667,9 @@
       kCard1ServerId, /*use_count=*/2, /*use_date=*/5);
 
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+  // Local changes should not cause local DB writes.
+  EXPECT_CALL(*backend(), CommitChanges()).Times(0);
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
 
   bridge()->AutofillProfileChanged(
       AutofillProfileChange(AutofillProfileChange::ADD,
@@ -706,6 +714,9 @@
       Put(kAddr1StorageKey, HasSpecifics(expected_profile_specifics), _));
   EXPECT_CALL(mock_processor(),
               Put(kCard1StorageKey, HasSpecifics(expected_card_specifics), _));
+  // Local changes should not cause local DB writes.
+  EXPECT_CALL(*backend(), CommitChanges()).Times(0);
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
 
   bridge()->AutofillProfileChanged(
       AutofillProfileChange(AutofillProfileChange::UPDATE,
@@ -740,6 +751,9 @@
       Put(kAddr1StorageKey, HasSpecifics(expected_profile_specifics), _));
   EXPECT_CALL(mock_processor(),
               Put(kCard1StorageKey, HasSpecifics(expected_card_specifics), _));
+  // Local changes should not cause local DB writes.
+  EXPECT_CALL(*backend(), CommitChanges()).Times(0);
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
 
   bridge()->AutofillProfileChanged(AutofillProfileChange(
       AutofillProfileChange::ADD, new_profile.server_id(), &new_profile));
@@ -774,6 +788,9 @@
       Put(kAddr1StorageKey, HasSpecifics(expected_profile_specifics), _));
   EXPECT_CALL(mock_processor(),
               Put(kCard1StorageKey, HasSpecifics(expected_card_specifics), _));
+  // Local changes should not cause local DB writes.
+  EXPECT_CALL(*backend(), CommitChanges()).Times(0);
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
 
   bridge()->AutofillProfileChanged(AutofillProfileChange(
       AutofillProfileChange::UPDATE, new_profile.server_id(), &new_profile));
@@ -799,6 +816,9 @@
 
   EXPECT_CALL(mock_processor(), Delete(kAddr1StorageKey, _));
   EXPECT_CALL(mock_processor(), Delete(kCard1StorageKey, _));
+  // Local changes should not cause local DB writes.
+  EXPECT_CALL(*backend(), CommitChanges()).Times(0);
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
 
   bridge()->AutofillProfileChanged(AutofillProfileChange(
       AutofillProfileChange::REMOVE, existing_profile.server_id(), nullptr));
@@ -825,6 +845,9 @@
   ASSERT_THAT(GetAllLocalDataInclRestart(), IsEmpty());
 
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  // Local changes should not cause local DB writes.
+  EXPECT_CALL(*backend(), CommitChanges()).Times(0);
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
 
   bridge()->AutofillProfileChanged(AutofillProfileChange(
       AutofillProfileChange::REMOVE, existing_profile.server_id(), nullptr));
@@ -855,6 +878,7 @@
 
   EXPECT_CALL(mock_processor(), Delete(kAddr1StorageKey, _));
   EXPECT_CALL(mock_processor(), Delete(kCard1StorageKey, _));
+  EXPECT_CALL(*backend(), CommitChanges());
 
   ResetBridge();
 
@@ -880,6 +904,8 @@
 
   // Since the entities are non-oprhans, they should not get deleted.
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(*backend(), CommitChanges()).Times(0);
+
   ResetBridge();
 
   EXPECT_THAT(
@@ -905,6 +931,7 @@
 
   // We do not advance time so the orphans are recent, should not get deleted.
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(*backend(), CommitChanges()).Times(0);
 
   ResetBridge();
 
@@ -947,6 +974,8 @@
   EXPECT_CALL(mock_processor(),
               Put(kCard1StorageKey, HasSpecifics(preexisting_card), _));
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ResetBridge(/*initial_sync_done=*/false);
   StartSyncing({remote_profile, remote_card});
@@ -990,6 +1019,8 @@
   EXPECT_CALL(mock_processor(),
               Put(kCard1StorageKey, HasSpecifics(preexisting_card), _));
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ResetBridge(/*initial_sync_done=*/false);
   StartSyncing({remote_profile, remote_card});
@@ -1020,6 +1051,7 @@
               UnorderedElementsAre(kAddr1StorageKey, kCard1StorageKey));
 
   // Now delete the profile. Changes should happen in the local database.
+  EXPECT_CALL(*backend(), CommitChanges());
   EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
   ReceiveTombstones({profile, card});
 
@@ -1054,9 +1086,11 @@
   ASSERT_THAT(GetLocalSyncMetadataStorageKeys(),
               UnorderedElementsAre(kAddr1StorageKey, kCard1StorageKey));
 
-  // Send a deletions from the server. Since the data is already deleted, it
-  // should not notify about changes. The entities should however get deleted
-  // from the processor.
+  // Send deletions from the server. We should commit to write the new progress
+  // marker down.
+  EXPECT_CALL(*backend(), CommitChanges());
+  // Since the data is already deleted, it should not notify about changes. The
+  // entities should however get deleted from the processor.
   EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
   ReceiveTombstones({profile, card});
 
@@ -1090,7 +1124,10 @@
   ASSERT_FALSE(real_processor()->IsTrackingEntityForTest(kCard1StorageKey));
   ASSERT_THAT(GetLocalSyncMetadataStorageKeys(), IsEmpty());
 
-  // Send a deletion from the server. The deletion should get ignored.
+  // Send deletions from the server. We should commit to write the new progress
+  // marker down.
+  EXPECT_CALL(*backend(), CommitChanges());
+  // The actual deletion should get ignored.
   EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
   ReceiveTombstones({profile, card});
 
@@ -1141,10 +1178,14 @@
 // No upstream communication or local DB change happens if the server sends an
 // empty update.
 TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest, EmptyUpdateIgnored) {
+  ResetBridgeWithPotentialInitialSync({});
+
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
+  // We still need to commit the updated progress marker.
+  EXPECT_CALL(*backend(), CommitChanges());
 
-  ResetBridgeWithPotentialInitialSync({});
   ReceivePotentiallyInitialUpdates({});
 
   EXPECT_THAT(GetAllLocalDataInclRestart(), IsEmpty());
@@ -1166,6 +1207,9 @@
 
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
+  // We still need to commit the updated progress marker.
+  EXPECT_CALL(*backend(), CommitChanges());
 
   ReceivePotentiallyInitialUpdates({profile, card});
 
@@ -1198,6 +1242,8 @@
 
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ReceivePotentiallyInitialUpdates(
       {updated_remote_profile, updated_remote_card});
@@ -1233,6 +1279,9 @@
   EXPECT_CALL(mock_processor(),
               Put(kAddr1StorageKey, HasSpecifics(profile), _));
   EXPECT_CALL(mock_processor(), Put(kCard1StorageKey, HasSpecifics(card), _));
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
+  // We still need to commit the updated progress marker.
+  EXPECT_CALL(*backend(), CommitChanges());
 
   ReceivePotentiallyInitialUpdates(
       {updated_remote_profile, updated_remote_card});
@@ -1276,6 +1325,8 @@
               Put(kAddr1StorageKey, HasSpecifics(merged_profile), _));
   EXPECT_CALL(mock_processor(),
               Put(kCard1StorageKey, HasSpecifics(merged_card), _));
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ReceivePotentiallyInitialUpdates(
       {updated_remote_profile, updated_remote_card});
@@ -1320,6 +1371,8 @@
               Put(kAddr1StorageKey, HasSpecifics(merged_profile), _));
   EXPECT_CALL(mock_processor(),
               Put(kCard1StorageKey, HasSpecifics(merged_card), _));
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ReceivePotentiallyInitialUpdates(
       {updated_remote_profile, updated_remote_card});
@@ -1353,6 +1406,8 @@
 
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ReceivePotentiallyInitialUpdates(
       {updated_remote_profile, updated_remote_card});
@@ -1382,6 +1437,8 @@
 
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ReceivePotentiallyInitialUpdates({updated_remote_card});
 
@@ -1409,6 +1466,9 @@
 
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(), Put(kCard1StorageKey, HasSpecifics(card), _));
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
+  // We still need to commit the updated progress marker.
+  EXPECT_CALL(*backend(), CommitChanges());
 
   ReceivePotentiallyInitialUpdates({updated_remote_card});
 
@@ -1436,6 +1496,8 @@
 
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ReceivePotentiallyInitialUpdates({updated_remote_card});
 
@@ -1463,6 +1525,9 @@
 
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(), Put(kCard1StorageKey, HasSpecifics(card), _));
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
+  // We still need to commit the updated progress marker.
+  EXPECT_CALL(*backend(), CommitChanges());
 
   ReceivePotentiallyInitialUpdates({updated_remote_card});
 
@@ -1490,6 +1555,8 @@
 
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ReceivePotentiallyInitialUpdates({updated_remote_card});
 
@@ -1517,6 +1584,9 @@
 
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(), Put(kCard1StorageKey, HasSpecifics(card), _));
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
+  // We still need to commit the updated progress marker.
+  EXPECT_CALL(*backend(), CommitChanges());
 
   ReceivePotentiallyInitialUpdates({updated_remote_card});
 
@@ -1553,6 +1623,8 @@
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(),
               Put(kCard1StorageKey, HasSpecifics(merged_card), _));
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ReceivePotentiallyInitialUpdates({updated_remote_card});
 
@@ -1589,6 +1661,8 @@
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(),
               Put(kCard1StorageKey, HasSpecifics(merged_card), _));
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ReceivePotentiallyInitialUpdates({updated_remote_card});
 
@@ -1616,6 +1690,8 @@
 
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ReceivePotentiallyInitialUpdates({updated_remote_profile});
 
@@ -1643,6 +1719,9 @@
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(),
               Put(kAddr1StorageKey, HasSpecifics(profile), _));
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges()).Times(0);
+  // We still need to commit the updated progress marker.
+  EXPECT_CALL(*backend(), CommitChanges());
 
   ReceivePotentiallyInitialUpdates({updated_remote_profile});
 
@@ -1677,6 +1756,8 @@
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(),
               Put(kAddr1StorageKey, HasSpecifics(merged_profile), _));
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ReceivePotentiallyInitialUpdates({updated_remote_profile});
 
@@ -1711,6 +1792,8 @@
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
   EXPECT_CALL(mock_processor(),
               Put(kAddr1StorageKey, HasSpecifics(merged_profile), _));
+  EXPECT_CALL(*backend(), CommitChanges());
+  EXPECT_CALL(*backend(), NotifyOfMultipleAutofillChanges());
 
   ReceivePotentiallyInitialUpdates({updated_remote_profile});
 
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index c89587fb..5c8ef56 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -299,6 +299,11 @@
 const base::Feature kAutofillSuppressDisusedCreditCards{
     "AutofillSuppressDisusedCreditCards", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Controls whether Autofill should search prefixes of all words/tokens when
+// filtering profiles, or only on prefixes of the whole string.
+const base::Feature kAutofillTokenPrefixMatching{
+    "AutofillTokenPrefixMatching", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kAutofillUploadThrottling{"AutofillUploadThrottling",
                                               base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index e9c0156..bccb13e 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -77,6 +77,7 @@
 extern const base::Feature kAutofillSkipComparingInferredLabels;
 extern const base::Feature kAutofillSuppressDisusedAddresses;
 extern const base::Feature kAutofillSuppressDisusedCreditCards;
+extern const base::Feature kAutofillTokenPrefixMatching;
 extern const base::Feature kAutofillUploadThrottling;
 extern const base::Feature kAutofillUpstream;
 extern const base::Feature kAutofillUpstreamAllowAllEmailDomains;
diff --git a/components/autofill/core/common/autofill_switches.cc b/components/autofill/core/common/autofill_switches.cc
index c0c551c4..cf033418 100644
--- a/components/autofill/core/common/autofill_switches.cc
+++ b/components/autofill/core/common/autofill_switches.cc
@@ -38,10 +38,6 @@
 const char kEnableOfferStoreUnmaskedWalletCards[] =
     "enable-offer-store-unmasked-wallet-cards";
 
-// Enables suggestions with substring matching instead of prefix matching.
-const char kEnableSuggestionsWithSubstringMatch[] =
-    "enable-suggestions-with-substring-match";
-
 // Ignores autocomplete="off" for Autofill data (profiles + credit cards).
 const char kIgnoreAutocompleteOffForAutofill[] =
     "ignore-autocomplete-off-autofill";
diff --git a/components/autofill/core/common/autofill_switches.h b/components/autofill/core/common/autofill_switches.h
index 2e14050..0e6bffe 100644
--- a/components/autofill/core/common/autofill_switches.h
+++ b/components/autofill/core/common/autofill_switches.h
@@ -18,7 +18,6 @@
 extern const char kAutofillUploadThrottlingPeriodInDays[];
 extern const char kDisableOfferStoreUnmaskedWalletCards[];
 extern const char kEnableOfferStoreUnmaskedWalletCards[];
-extern const char kEnableSuggestionsWithSubstringMatch[];
 extern const char kIgnoreAutocompleteOffForAutofill[];
 extern const char kShowAutofillTypePredictions[];
 extern const char kShowAutofillSignatures[];
diff --git a/components/autofill/core/common/autofill_util.cc b/components/autofill/core/common/autofill_util.cc
index 0191e66..adf79a5 100644
--- a/components/autofill/core/common/autofill_util.cc
+++ b/components/autofill/core/common/autofill_util.cc
@@ -51,8 +51,7 @@
 }  // namespace
 
 bool IsFeatureSubstringMatchEnabled() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableSuggestionsWithSubstringMatch);
+  return base::FeatureList::IsEnabled(features::kAutofillTokenPrefixMatching);
 }
 
 bool IsShowAutofillSignaturesEnabled() {
diff --git a/components/autofill/core/common/autofill_util_unittest.cc b/components/autofill/core/common/autofill_util_unittest.cc
index e240c5c4..4f1430a 100644
--- a/components/autofill/core/common/autofill_util_unittest.cc
+++ b/components/autofill/core/common/autofill_util_unittest.cc
@@ -9,7 +9,8 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
-#include "components/autofill/core/common/autofill_switches.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace autofill {
@@ -27,14 +28,19 @@
 
 TEST_P(FieldIsTokenBoundarySubstringCaseTest,
        FieldIsSuggestionSubstringStartingOnTokenBoundary) {
-  // FieldIsSuggestionSubstringStartingOnTokenBoundary should not work yet
-  // without a flag.
-  EXPECT_FALSE(FieldIsSuggestionSubstringStartingOnTokenBoundary(
-      base::ASCIIToUTF16("ab@cd.b"), base::ASCIIToUTF16("b"), false));
+  {
+    base::test::ScopedFeatureList features_disabled;
+    features_disabled.InitAndDisableFeature(
+        features::kAutofillTokenPrefixMatching);
 
-  // Token matching is currently behind a flag.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableSuggestionsWithSubstringMatch);
+    // FieldIsSuggestionSubstringStartingOnTokenBoundary should not work yet
+    // without a flag.
+    EXPECT_FALSE(FieldIsSuggestionSubstringStartingOnTokenBoundary(
+        base::ASCIIToUTF16("ab@cd.b"), base::ASCIIToUTF16("b"), false));
+  }
+
+  base::test::ScopedFeatureList features_enabled;
+  features_enabled.InitAndEnableFeature(features::kAutofillTokenPrefixMatching);
 
   auto test_case = GetParam();
   SCOPED_TRACE(testing::Message()
diff --git a/components/certificate_transparency/log_dns_client.cc b/components/certificate_transparency/log_dns_client.cc
index 3d47b4a..783b85d 100644
--- a/components/certificate_transparency/log_dns_client.cc
+++ b/components/certificate_transparency/log_dns_client.cc
@@ -535,7 +535,7 @@
     bool lookup_securely,
     uint64_t tree_size,
     std::unique_ptr<AuditProofQuery>* out_query,
-    const net::CompletionCallback& callback) {
+    net::CompletionOnceCallback callback) {
   DCHECK(out_query);
 
   if (domain_for_log.empty() || leaf_hash.size() != crypto::kSHA256Length) {
@@ -552,15 +552,16 @@
 
   ++in_flight_queries_;
 
-  return query->Start(std::move(leaf_hash), lookup_securely, tree_size,
-                      base::BindOnce(&LogDnsClient::QueryAuditProofComplete,
-                                     base::Unretained(this), callback),
-                      base::BindOnce(&LogDnsClient::QueryAuditProofCancelled,
-                                     base::Unretained(this)));
+  return query->Start(
+      std::move(leaf_hash), lookup_securely, tree_size,
+      base::BindOnce(&LogDnsClient::QueryAuditProofComplete,
+                     base::Unretained(this), std::move(callback)),
+      base::BindOnce(&LogDnsClient::QueryAuditProofCancelled,
+                     base::Unretained(this)));
 }
 
 void LogDnsClient::QueryAuditProofComplete(
-    const net::CompletionCallback& completion_callback,
+    net::CompletionOnceCallback completion_callback,
     int net_error) {
   --in_flight_queries_;
 
@@ -569,7 +570,7 @@
   std::list<base::OnceClosure> not_throttled_callbacks =
       std::move(not_throttled_callbacks_);
 
-  completion_callback.Run(net_error);
+  std::move(completion_callback).Run(net_error);
 
   // Notify interested parties that the next query will not be throttled.
   for (auto& callback : not_throttled_callbacks) {
diff --git a/components/certificate_transparency/log_dns_client.h b/components/certificate_transparency/log_dns_client.h
index 0376f61..1dd69f5 100644
--- a/components/certificate_transparency/log_dns_client.h
+++ b/components/certificate_transparency/log_dns_client.h
@@ -103,13 +103,13 @@
                              bool lookup_securely,
                              uint64_t tree_size,
                              std::unique_ptr<AuditProofQuery>* out_query,
-                             const net::CompletionCallback& callback);
+                             net::CompletionOnceCallback callback);
 
  private:
   // Invoked when an audit proof query completes.
   // |callback| is the user-provided callback that should be notified.
   // |net_error| is a net::Error indicating success or failure.
-  void QueryAuditProofComplete(const net::CompletionCallback& callback,
+  void QueryAuditProofComplete(net::CompletionOnceCallback callback,
                                int net_error);
 
   // Invoked when an audit proof query is cancelled.
diff --git a/components/certificate_transparency/mock_log_dns_traffic.h b/components/certificate_transparency/mock_log_dns_traffic.h
index d70119e9..d6cefb1d 100644
--- a/components/certificate_transparency/mock_log_dns_traffic.h
+++ b/components/certificate_transparency/mock_log_dns_traffic.h
@@ -46,7 +46,7 @@
 //     "123456");
 //
 // LogDnsClient log_client(mock_dns.CreateDnsClient(), ...);
-// log_client.QueryAuditProof("ct.test", ..., base::Bind(...));
+// log_client.QueryAuditProof("ct.test", ..., base::BindOnce(...));
 class MockLogDnsTraffic {
  public:
   MockLogDnsTraffic();
diff --git a/components/certificate_transparency/single_tree_tracker.cc b/components/certificate_transparency/single_tree_tracker.cc
index 854c0890..90794fb 100644
--- a/components/certificate_transparency/single_tree_tracker.cc
+++ b/components/certificate_transparency/single_tree_tracker.cc
@@ -410,8 +410,8 @@
     net::Error result = dns_client_->QueryAuditProof(
         ct_log_->dns_domain(), leaf_hash, it->first.lookup_securely,
         verified_sth_.tree_size, &(it->second.audit_proof_query),
-        base::Bind(&SingleTreeTracker::OnAuditProofObtained,
-                   base::Unretained(this), it->first));
+        base::BindOnce(&SingleTreeTracker::OnAuditProofObtained,
+                       base::Unretained(this), it->first));
     // Handling proofs returned synchronously is not implemeted.
     DCHECK_NE(result, net::OK);
     if (result == net::ERR_IO_PENDING) {
diff --git a/components/data_reduction_proxy/content/browser/content_lofi_ui_service.cc b/components/data_reduction_proxy/content/browser/content_lofi_ui_service.cc
index a920d97..b19ac89 100644
--- a/components/data_reduction_proxy/content/browser/content_lofi_ui_service.cc
+++ b/components/data_reduction_proxy/content/browser/content_lofi_ui_service.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
+#include "build/build_config.h"
 #include "components/previews/core/previews_experiments.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
@@ -34,8 +35,10 @@
 
   // If the UI is in the Android Omnibox, it has already been shown at commit
   // time.
+#if defined(OS_ANDROID)
   if (previews::params::IsPreviewsOmniboxUiEnabled())
     return;
+#endif
 
   int render_process_id = -1;
   int render_frame_id = -1;
@@ -53,9 +56,11 @@
     int render_process_id,
     int render_frame_id) {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
+#if defined(OS_ANDROID)
   // If the UI is in the Android Omnibox, it has already been shown at commit
   // time.
   DCHECK(!previews::params::IsPreviewsOmniboxUiEnabled());
+#endif
 
   content::RenderFrameHost* frame =
       content::RenderFrameHost::FromID(render_process_id, render_frame_id);
diff --git a/components/data_reduction_proxy/content/browser/content_lofi_ui_service_unittest.cc b/components/data_reduction_proxy/content/browser/content_lofi_ui_service_unittest.cc
index d1ed6bc..b205ded9 100644
--- a/components/data_reduction_proxy/content/browser/content_lofi_ui_service_unittest.cc
+++ b/components/data_reduction_proxy/content/browser/content_lofi_ui_service_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/task/post_task.h"
+#include "build/build_config.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
@@ -98,6 +99,7 @@
   bool callback_called_;
 };
 
+#if !defined(OS_ANDROID)
 TEST_F(ContentLoFiUIServiceTest, OnLoFiResponseReceived) {
   base::RunLoop ui_run_loop;
   base::PostTaskWithTraits(
@@ -108,5 +110,6 @@
   base::RunLoop().RunUntilIdle();
   VerifyOnLoFiResponseReceivedCallback();
 }
+#endif
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
index ceaa57ba..257401f 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -487,16 +487,17 @@
   // Sanity check the inputs, as this data may originate from a lower-privilege
   // process (renderer).
 
-  // The current policy sets this to 5 minutes, so don't allow a bigger
-  // timespan.
-  if (bypass_duration < base::TimeDelta() ||
-      bypass_duration > base::TimeDelta::FromMinutes(5)) {
+  if (bypass_duration < base::TimeDelta()) {
     LOG(ERROR) << "Received bad MarkProxiesAsBad() -- invalid bypass_duration: "
                << bypass_duration;
     std::move(callback).Run();
     return;
   }
 
+  // Limit maximum bypass duration to a day.
+  if (bypass_duration > base::TimeDelta::FromDays(1))
+    bypass_duration = base::TimeDelta::FromDays(1);
+
   // |bad_proxies| should be DRP servers or this API allows marking arbitrary
   // proxies as bad. It is possible that proxies from an older config are
   // received (FindConfiguredDataReductionProxy() searches recent proxies too).
diff --git a/components/exo/wayland/clients/client_base.cc b/components/exo/wayland/clients/client_base.cc
index 8679f43..f7a4f759 100644
--- a/components/exo/wayland/clients/client_base.cc
+++ b/components/exo/wayland/clients/client_base.cc
@@ -787,10 +787,10 @@
 
     buffer->params.reset(
         zwp_linux_dmabuf_v1_create_params(globals_.linux_dmabuf.get()));
-    for (size_t i = 0; i < gbm_bo_get_num_planes(buffer->bo.get()); ++i) {
+    for (size_t i = 0; i < gbm_bo_get_plane_count(buffer->bo.get()); ++i) {
       base::ScopedFD fd(gbm_bo_get_plane_fd(buffer->bo.get(), i));
-      uint32_t stride = gbm_bo_get_plane_stride(buffer->bo.get(), i);
-      uint32_t offset = gbm_bo_get_plane_offset(buffer->bo.get(), i);
+      uint32_t stride = gbm_bo_get_stride_for_plane(buffer->bo.get(), i);
+      uint32_t offset = gbm_bo_get_offset(buffer->bo.get(), i);
       zwp_linux_buffer_params_v1_add(buffer->params.get(), fd.get(), i, offset,
                                      stride, 0, 0);
     }
@@ -801,22 +801,23 @@
     buffer->buffer.reset(zwp_linux_buffer_params_v1_create_immed(
         buffer->params.get(), size.width(), size.height(), drm_format, flags));
 
-    if (gbm_bo_get_num_planes(buffer->bo.get()) != 1)
+    if (gbm_bo_get_plane_count(buffer->bo.get()) != 1)
       return buffer;
 
-    EGLint khr_image_attrs[] = {EGL_DMA_BUF_PLANE0_FD_EXT,
-                                fd.get(),
-                                EGL_WIDTH,
-                                size.width(),
-                                EGL_HEIGHT,
-                                size.height(),
-                                EGL_LINUX_DRM_FOURCC_EXT,
-                                drm_format,
-                                EGL_DMA_BUF_PLANE0_PITCH_EXT,
-                                gbm_bo_get_plane_stride(buffer->bo.get(), 0),
-                                EGL_DMA_BUF_PLANE0_OFFSET_EXT,
-                                0,
-                                EGL_NONE};
+    EGLint khr_image_attrs[] = {
+        EGL_DMA_BUF_PLANE0_FD_EXT,
+        fd.get(),
+        EGL_WIDTH,
+        size.width(),
+        EGL_HEIGHT,
+        size.height(),
+        EGL_LINUX_DRM_FOURCC_EXT,
+        drm_format,
+        EGL_DMA_BUF_PLANE0_PITCH_EXT,
+        gbm_bo_get_stride_for_plane(buffer->bo.get(), 0),
+        EGL_DMA_BUF_PLANE0_OFFSET_EXT,
+        0,
+        EGL_NONE};
     EGLImageKHR image = eglCreateImageKHR(
         eglGetCurrentDisplay(), EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT,
         nullptr /* no client buffer */, khr_image_attrs);
diff --git a/components/exo/wayland/clients/yuv.cc b/components/exo/wayland/clients/yuv.cc
index ec8e634f..ceff47c2 100644
--- a/components/exo/wayland/clients/yuv.cc
+++ b/components/exo/wayland/clients/yuv.cc
@@ -35,10 +35,10 @@
 };
 
 bool YuvClient::WriteSolidColor(gbm_bo* bo, SkColor color) {
-  for (size_t i = 0; i < gbm_bo_get_num_planes(bo); ++i) {
+  for (size_t i = 0; i < gbm_bo_get_plane_count(bo); ++i) {
     base::ScopedFD fd(gbm_bo_get_plane_fd(bo, i));
-    uint32_t stride = gbm_bo_get_plane_stride(bo, i);
-    uint32_t offset = gbm_bo_get_plane_offset(bo, i);
+    uint32_t stride = gbm_bo_get_stride_for_plane(bo, i);
+    uint32_t offset = gbm_bo_get_offset(bo, i);
     uint32_t map_size = gbm_bo_get_plane_size(bo, i) + offset;
     void* void_data = mmap(nullptr, map_size, (PROT_READ | PROT_WRITE),
                            MAP_SHARED, fd.get(), 0);
diff --git a/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/FakeModuleInstallerBackend.java b/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/FakeModuleInstallerBackend.java
index a0c2ffa..dae242dc 100644
--- a/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/FakeModuleInstallerBackend.java
+++ b/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/FakeModuleInstallerBackend.java
@@ -71,9 +71,28 @@
     private boolean installInternal(String moduleName) {
         Context context = ContextUtils.getApplicationContext();
         int versionCode = BuildInfo.getInstance().versionCode;
+
         // Get list of all files at path where SplitCompat looks for downloaded modules.
         // May change in future releases of the Play Core SDK.
-        File[] srcModuleFiles = new File(MODULES_SRC_DIRECTORY_PATH).listFiles();
+        File srcModuleDir = new File(MODULES_SRC_DIRECTORY_PATH);
+        if (!srcModuleDir.exists()) {
+            Log.e(TAG, "Modules source directory does not exist");
+            return false;
+        }
+        if (!srcModuleDir.canRead()) {
+            Log.e(TAG, "Cannot read modules source directory");
+            return false;
+        }
+        if (!srcModuleDir.isDirectory()) {
+            Log.e(TAG, "Modules source directory is not a directory");
+            return false;
+        }
+        File[] srcModuleFiles = srcModuleDir.listFiles();
+        if (srcModuleFiles == null) {
+            Log.e(TAG, "Cannot get list of files in modules source directory");
+            return false;
+        }
+
         // Check if any apks for the module are actually installed.
         boolean no_module_apks_installed = true;
 
@@ -110,6 +129,7 @@
         }
 
         if (no_module_apks_installed) {
+            Log.e(TAG, "Did not find any module APKs");
             return false;
         }
 
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index df6e08e..624728d7 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -514,11 +514,10 @@
 
 bool ZeroSuggestProvider::AllowZeroSuggestSuggestions(
     const GURL& current_page_url) const {
-  // Don't show zero suggest on the NTP.
-  // TODO(hfung): Experiment with showing MostVisited zero suggest on NTP
-  // under the conditions described in crbug.com/305366.
-  if (IsNTPPage(current_page_classification_))
+  if (IsNTPPage(current_page_classification_) &&
+      !base::FeatureList::IsEnabled(omnibox::kZeroSuggestionsOnNTP)) {
     return false;
+  }
 
   // Don't run if in incognito mode.
   if (client()->IsOffTheRecord())
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 4b5325a..9486d34 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -273,4 +273,9 @@
 const base::Feature kOmniboxMaterialDesignWeatherIcons{
     "OmniboxMaterialDesignWeatherIcons", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Allow suggestions to be shown to the user on the New Tab Page upon focusing
+// URL bar (the omnibox).
+const base::Feature kZeroSuggestionsOnNTP{"OmniboxZeroSuggestionsOnNTP",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace omnibox
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index cfaa2509..4b07be28 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -47,6 +47,7 @@
 extern const base::Feature kDedupeGoogleDriveURLs;
 extern const base::Feature kOmniboxPopupShortcutIconsInZeroState;
 extern const base::Feature kOmniboxMaterialDesignWeatherIcons;
+extern const base::Feature kZeroSuggestionsOnNTP;
 
 }  // namespace omnibox
 
diff --git a/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
index a9ccbb3..37fafdca 100644
--- a/components/password_manager/core/browser/password_autofill_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/metrics/user_action_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "build/build_config.h"
 #include "components/autofill/core/browser/popup_item_ids.h"
@@ -20,7 +21,7 @@
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/browser/test_autofill_driver.h"
 #include "components/autofill/core/common/autofill_constants.h"
-#include "components/autofill/core/common/autofill_switches.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/autofill/core/common/password_form_fill_data.h"
 #include "components/favicon/core/test/mock_favicon_service.h"
@@ -429,9 +430,9 @@
 // Verify that typing "foo" into the username field will match usernames
 // "foo.bar@example.com", "bar.foo@example.com" and "example@foo.com".
 TEST_F(PasswordAutofillManagerTest, DisplaySuggestionsWithMatchingTokens) {
-  // Token matching is currently behind a flag.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      autofill::switches::kEnableSuggestionsWithSubstringMatch);
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(
+      autofill::features::kAutofillTokenPrefixMatching);
 
   std::unique_ptr<TestPasswordManagerClient> client(
       new TestPasswordManagerClient);
@@ -465,9 +466,9 @@
 // Verify that typing "oo" into the username field will not match any usernames
 // "foo.bar@example.com", "bar.foo@example.com" or "example@foo.com".
 TEST_F(PasswordAutofillManagerTest, NoSuggestionForNonPrefixTokenMatch) {
-  // Token matching is currently behind a flag.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      autofill::switches::kEnableSuggestionsWithSubstringMatch);
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(
+      autofill::features::kAutofillTokenPrefixMatching);
 
   std::unique_ptr<TestPasswordManagerClient> client(
       new TestPasswordManagerClient);
@@ -499,9 +500,9 @@
 // tokens.
 TEST_F(PasswordAutofillManagerTest,
        MatchingContentsWithSuggestionTokenSeparator) {
-  // Token matching is currently behind a flag.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      autofill::switches::kEnableSuggestionsWithSubstringMatch);
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(
+      autofill::features::kAutofillTokenPrefixMatching);
 
   std::unique_ptr<TestPasswordManagerClient> client(
       new TestPasswordManagerClient);
@@ -538,9 +539,9 @@
 // i.e. prefix matched followed by substring matched.
 TEST_F(PasswordAutofillManagerTest,
        DisplaySuggestionsWithPrefixesPrecedeSubstringMatched) {
-  // Token matching is currently behind a flag.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      autofill::switches::kEnableSuggestionsWithSubstringMatch);
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(
+      autofill::features::kAutofillTokenPrefixMatching);
 
   std::unique_ptr<TestPasswordManagerClient> client(
       new TestPasswordManagerClient);
diff --git a/components/policy/core/browser/android/policy_converter.cc b/components/policy/core/browser/android/policy_converter.cc
index b711a64f..9d41ad0 100644
--- a/components/policy/core/browser/android/policy_converter.cc
+++ b/components/policy/core/browser/android/policy_converter.cc
@@ -175,10 +175,17 @@
       }
       return value;
     }
+
+    // TODO(crbug.com/859477): Remove after root cause is found.
+    case base::Value::Type::DEAD: {
+      CHECK(false);
+      return nullptr;
+    }
   }
 
-  NOTREACHED();
-  return std::unique_ptr<base::Value>();
+  // TODO(crbug.com/859477): Revert to NOTREACHED() after root cause is found.
+  CHECK(false);
+  return nullptr;
 }
 
 void PolicyConverter::SetPolicyValue(const std::string& key,
diff --git a/components/policy/core/common/policy_loader_win_unittest.cc b/components/policy/core/common/policy_loader_win_unittest.cc
index 311e7fb..0377307 100644
--- a/components/policy/core/common/policy_loader_win_unittest.cc
+++ b/components/policy/core/common/policy_loader_win_unittest.cc
@@ -133,8 +133,14 @@
 
     case base::Value::Type::BINARY:
       return false;
+
+    // TODO(crbug.com/859477): Remove after root cause is found.
+    case base::Value::Type::DEAD:
+      CHECK(false);
+      return false;
   }
-  NOTREACHED();
+  // TODO(crbug.com/859477): Revert to NOTREACHED() after root cause is found.
+  CHECK(false);
   return false;
 }
 
diff --git a/components/policy/core/common/policy_test_utils.cc b/components/policy/core/common/policy_test_utils.cc
index 5af98b4..919f004 100644
--- a/components/policy/core/common/policy_test_utils.cc
+++ b/components/policy/core/common/policy_test_utils.cc
@@ -137,6 +137,11 @@
       // because there's no equivalent JSON type, and policy values can only
       // take valid JSON values.
       break;
+
+    // TODO(crbug.com/859477): Remove after root cause is found.
+    case base::Value::Type::DEAD:
+      CHECK(false);
+      break;
   }
 
   return NULL;
diff --git a/components/policy/core/common/registry_dict.cc b/components/policy/core/common/registry_dict.cc
index f3ed372b..696ba7e 100644
--- a/components/policy/core/common/registry_dict.cc
+++ b/components/policy/core/common/registry_dict.cc
@@ -135,6 +135,10 @@
     case base::Value::Type::BINARY:
       // No conversion possible.
       break;
+    // TODO(crbug.com/859477): Remove after root cause is found.
+    case base::Value::Type::DEAD:
+      CHECK(false);
+      return nullptr;
   }
 
   LOG(WARNING) << "Failed to convert " << value.type() << " to "
diff --git a/components/previews/core/previews_features.cc b/components/previews/core/previews_features.cc
index 4e8ce0e..d847338d 100644
--- a/components/previews/core/previews_features.cc
+++ b/components/previews/core/previews_features.cc
@@ -90,7 +90,7 @@
 // Shows a Previews icon and string in the Android Omnibox instead of an Infobar
 // when enabled. Only works and is honored on Android..
 const base::Feature kAndroidOmniboxPreviewsBadge{
-    "AndroidOmniboxPreviewsBadge", base::FEATURE_DISABLED_BY_DEFAULT};
+    "AndroidOmniboxPreviewsBadge", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Provides slow page triggering parameters.
 const base::Feature kSlowPageTriggering{"PreviewsSlowPageTriggering",
diff --git a/components/safe_browsing/android/remote_database_manager_unittest.cc b/components/safe_browsing/android/remote_database_manager_unittest.cc
index 95857de..b127a5d 100644
--- a/components/safe_browsing/android/remote_database_manager_unittest.cc
+++ b/components/safe_browsing/android/remote_database_manager_unittest.cc
@@ -23,7 +23,7 @@
 
 class TestSafeBrowsingApiHandler : public SafeBrowsingApiHandler {
  public:
-  std::string GetSafetyNetId() const override { return ""; }
+  std::string GetSafetyNetId() override { return ""; }
   void StartURLCheck(std::unique_ptr<URLCheckCallbackMeta> callback,
                      const GURL& url,
                      const SBThreatTypeSet& threat_types) override {}
diff --git a/components/safe_browsing/android/safe_browsing_api_handler.h b/components/safe_browsing/android/safe_browsing_api_handler.h
index 04385c2..c82a765 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler.h
+++ b/components/safe_browsing/android/safe_browsing_api_handler.h
@@ -30,7 +30,7 @@
       URLCheckCallbackMeta;
 
   // Returns the Safety Net ID of the device.
-  virtual std::string GetSafetyNetId() const = 0;
+  virtual std::string GetSafetyNetId() = 0;
   // Makes Native->Java call and invokes callback when check is done.
   virtual void StartURLCheck(std::unique_ptr<URLCheckCallbackMeta> callback,
                              const GURL& url,
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc b/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
index 74f2722..f6580534 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
+++ b/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
@@ -224,17 +224,14 @@
   return j_api_handler_.obj() != nullptr;
 }
 
-std::string SafeBrowsingApiHandlerBridge::GetSafetyNetId() const {
+std::string SafeBrowsingApiHandlerBridge::GetSafetyNetId() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   bool feature_enabled =
       base::FeatureList::IsEnabled(kTelemetryForApkDownloads);
   DCHECK(feature_enabled);
 
-  if (!feature_enabled)
-    return "";
-
   static std::string safety_net_id;
-  if (safety_net_id.empty()) {
+  if (feature_enabled && CheckApiIsSupported() && safety_net_id.empty()) {
     JNIEnv* env = AttachCurrentThread();
     ScopedJavaLocalRef<jstring> jsafety_net_id =
         Java_SafeBrowsingApiBridge_getSafetyNetId(env, j_api_handler_);
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_bridge.h b/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
index 99262aaf..bc8da6f 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
+++ b/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
@@ -25,7 +25,7 @@
   SafeBrowsingApiHandlerBridge();
   ~SafeBrowsingApiHandlerBridge() override;
 
-  std::string GetSafetyNetId() const override;
+  std::string GetSafetyNetId() override;
 
   // Makes Native->Java call to check the URL against Safe Browsing lists.
   void StartURLCheck(std::unique_ptr<URLCheckCallbackMeta> callback,
diff --git a/components/viz/service/display_embedder/buffer_queue.cc b/components/viz/service/display_embedder/buffer_queue.cc
index 679338a..6f00149 100644
--- a/components/viz/service/display_embedder/buffer_queue.cc
+++ b/components/viz/service/display_embedder/buffer_queue.cc
@@ -21,14 +21,16 @@
 namespace viz {
 
 BufferQueue::BufferQueue(gpu::gles2::GLES2Interface* gl,
-                         uint32_t texture_target,
                          gfx::BufferFormat format,
                          gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
-                         gpu::SurfaceHandle surface_handle)
+                         gpu::SurfaceHandle surface_handle,
+                         const gpu::Capabilities& capabilities)
     : gl_(gl),
       fbo_(0),
       allocated_count_(0),
-      texture_target_(texture_target),
+      texture_target_(gpu::GetBufferTextureTarget(gfx::BufferUsage::SCANOUT,
+                                                  format,
+                                                  capabilities)),
       internal_format_(gpu::InternalFormatForGpuMemoryBufferFormat(format)),
       format_(format),
       gpu_memory_buffer_manager_(gpu_memory_buffer_manager),
diff --git a/components/viz/service/display_embedder/buffer_queue.h b/components/viz/service/display_embedder/buffer_queue.h
index 40ee5bb..990cb2a 100644
--- a/components/viz/service/display_embedder/buffer_queue.h
+++ b/components/viz/service/display_embedder/buffer_queue.h
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "components/viz/service/viz_service_export.h"
+#include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/color_space.h"
@@ -41,10 +42,10 @@
 class VIZ_SERVICE_EXPORT BufferQueue {
  public:
   BufferQueue(gpu::gles2::GLES2Interface* gl,
-              uint32_t texture_target,
               gfx::BufferFormat format,
               gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
-              gpu::SurfaceHandle surface_handle);
+              gpu::SurfaceHandle surface_handle,
+              const gpu::Capabilities& capabilities);
   virtual ~BufferQueue();
 
   void Initialize();
@@ -63,6 +64,7 @@
   uint32_t fbo() const { return fbo_; }
   uint32_t internal_format() const { return internal_format_; }
   gfx::BufferFormat buffer_format() const { return format_; }
+  uint32_t texture_target() const { return texture_target_; }
 
  private:
   friend class BufferQueueTest;
diff --git a/components/viz/service/display_embedder/buffer_queue_unittest.cc b/components/viz/service/display_embedder/buffer_queue_unittest.cc
index 4674600..14278c2 100644
--- a/components/viz/service/display_embedder/buffer_queue_unittest.cc
+++ b/components/viz/service/display_embedder/buffer_queue_unittest.cc
@@ -108,12 +108,12 @@
  public:
   MockBufferQueue(gpu::gles2::GLES2Interface* gl,
                   gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
-                  unsigned int target)
+                  const gpu::Capabilities& capabilities)
       : BufferQueue(gl,
-                    target,
                     kBufferQueueFormat,
                     gpu_memory_buffer_manager,
-                    kFakeSurfaceHandle) {}
+                    kFakeSurfaceHandle,
+                    capabilities) {}
   MOCK_METHOD4(CopyBufferDamage,
                void(int, int, const gfx::Rect&, const gfx::Rect&));
 };
@@ -130,9 +130,9 @@
     context_provider_ = TestContextProvider::Create(std::move(context));
     context_provider_->BindToCurrentThread();
     gpu_memory_buffer_manager_.reset(new StubGpuMemoryBufferManager);
-    mock_output_surface_ =
-        new MockBufferQueue(context_provider_->ContextGL(),
-                            gpu_memory_buffer_manager_.get(), GL_TEXTURE_2D);
+    mock_output_surface_ = new MockBufferQueue(
+        context_provider_->ContextGL(), gpu_memory_buffer_manager_.get(),
+        context_provider_->ContextCapabilities());
     output_surface_.reset(mock_output_surface_);
     output_surface_->Initialize();
   }
@@ -271,12 +271,12 @@
 }
 
 std::unique_ptr<BufferQueue> CreateBufferQueue(
-    unsigned int target,
     gpu::gles2::GLES2Interface* gl,
-    gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager) {
+    gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+    const gpu::Capabilities& capabilities) {
   std::unique_ptr<BufferQueue> buffer_queue(
-      new BufferQueue(gl, target, kBufferQueueFormat, gpu_memory_buffer_manager,
-                      kFakeSurfaceHandle));
+      new BufferQueue(gl, kBufferQueueFormat, gpu_memory_buffer_manager,
+                      kFakeSurfaceHandle, capabilities));
   buffer_queue->Initialize();
   return buffer_queue;
 }
@@ -287,9 +287,9 @@
       CreateMockedContextProvider(&context);
   std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager(
       new StubGpuMemoryBufferManager);
-  std::unique_ptr<BufferQueue> output_surface =
-      CreateBufferQueue(GL_TEXTURE_2D, context_provider->ContextGL(),
-                        gpu_memory_buffer_manager.get());
+  std::unique_ptr<BufferQueue> output_surface = CreateBufferQueue(
+      context_provider->ContextGL(), gpu_memory_buffer_manager.get(),
+      context_provider->ContextCapabilities());
 
   EXPECT_CALL(*context, BindFramebuffer(GL_FRAMEBUFFER, Ne(0U)));
   ON_CALL(*context, FramebufferTexture2D(_, _, _, _, _))
@@ -299,37 +299,35 @@
 }
 
 TEST(BufferQueueStandaloneTest, FboBinding) {
-  GLenum targets[] = {GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_ARB};
-  for (size_t i = 0; i < 2; ++i) {
-    GLenum target = targets[i];
+  MockedContext* context;
+  scoped_refptr<TestContextProvider> context_provider =
+      CreateMockedContextProvider(&context);
+  std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager(
+      new StubGpuMemoryBufferManager);
+  std::unique_ptr<BufferQueue> output_surface = CreateBufferQueue(
+      context_provider->ContextGL(), gpu_memory_buffer_manager.get(),
+      context_provider->ContextCapabilities());
 
-    MockedContext* context;
-    scoped_refptr<TestContextProvider> context_provider =
-        CreateMockedContextProvider(&context);
-    std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager(
-        new StubGpuMemoryBufferManager);
-    std::unique_ptr<BufferQueue> output_surface = CreateBufferQueue(
-        target, context_provider->ContextGL(), gpu_memory_buffer_manager.get());
+  GLenum target = output_surface->texture_target();
 
-    EXPECT_CALL(*context, BindTexture(target, Ne(0U)));
-    EXPECT_CALL(*context, DestroyImageCHROMIUM(1));
-    Expectation image =
-        EXPECT_CALL(*context,
-                    CreateImageCHROMIUM(_, 0, 0, kBufferQueueInternalformat))
-            .WillOnce(Return(1));
-    Expectation fb =
-        EXPECT_CALL(*context, BindFramebuffer(GL_FRAMEBUFFER, Ne(0U)));
-    Expectation tex = EXPECT_CALL(*context, BindTexture(target, Ne(0U)));
-    Expectation bind_tex =
-        EXPECT_CALL(*context, BindTexImage2DCHROMIUM(target, 1))
-            .After(tex, image);
-    EXPECT_CALL(*context,
-                FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-                                     target, Ne(0U), _))
-        .After(fb, bind_tex);
+  EXPECT_CALL(*context, BindTexture(target, Ne(0U)));
+  EXPECT_CALL(*context, DestroyImageCHROMIUM(1));
+  Expectation image =
+      EXPECT_CALL(*context,
+                  CreateImageCHROMIUM(_, 0, 0, kBufferQueueInternalformat))
+          .WillOnce(Return(1));
+  Expectation fb =
+      EXPECT_CALL(*context, BindFramebuffer(GL_FRAMEBUFFER, Ne(0U)));
+  Expectation tex = EXPECT_CALL(*context, BindTexture(target, Ne(0U)));
+  Expectation bind_tex =
+      EXPECT_CALL(*context, BindTexImage2DCHROMIUM(target, 1))
+          .After(tex, image);
+  EXPECT_CALL(*context,
+              FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target,
+                                   Ne(0U), _))
+      .After(fb, bind_tex);
 
-    output_surface->BindFramebuffer();
-  }
+  output_surface->BindFramebuffer();
 }
 
 TEST(BufferQueueStandaloneTest, CheckBoundFramebuffer) {
@@ -340,9 +338,10 @@
   std::unique_ptr<BufferQueue> output_surface;
   gpu_memory_buffer_manager.reset(new StubGpuMemoryBufferManager);
 
-  output_surface.reset(new BufferQueue(
-      context_provider->ContextGL(), GL_TEXTURE_2D, kBufferQueueFormat,
-      gpu_memory_buffer_manager.get(), kFakeSurfaceHandle));
+  output_surface.reset(
+      new BufferQueue(context_provider->ContextGL(), kBufferQueueFormat,
+                      gpu_memory_buffer_manager.get(), kFakeSurfaceHandle,
+                      context_provider->ContextCapabilities()));
   output_surface->Initialize();
   output_surface->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
   // Trigger a sub-buffer copy to exercise all paths.
diff --git a/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc b/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc
index 9420173b..e017557 100644
--- a/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc
@@ -22,7 +22,6 @@
     gpu::SurfaceHandle surface_handle,
     SyntheticBeginFrameSource* synthetic_begin_frame_source,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
-    uint32_t target,
     gfx::BufferFormat buffer_format)
     : GLOutputSurface(context_provider, synthetic_begin_frame_source) {
   capabilities_.uses_default_gl_framebuffer = false;
@@ -37,8 +36,8 @@
   capabilities_.max_frames_pending = 2;
 
   buffer_queue_ = std::make_unique<BufferQueue>(
-      context_provider->ContextGL(), target, buffer_format,
-      gpu_memory_buffer_manager, surface_handle);
+      context_provider->ContextGL(), buffer_format, gpu_memory_buffer_manager,
+      surface_handle, context_provider->ContextCapabilities());
   buffer_queue_->Initialize();
 }
 
diff --git a/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h b/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h
index 94420b205..d46caa8 100644
--- a/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h
+++ b/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h
@@ -37,7 +37,6 @@
       gpu::SurfaceHandle surface_handle,
       SyntheticBeginFrameSource* synthetic_begin_frame_source,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
-      uint32_t target,
       gfx::BufferFormat buffer_format);
 
   ~GLOutputSurfaceBufferQueue() override;
diff --git a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
index 391f9265..3a3b875 100644
--- a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
@@ -18,7 +18,6 @@
                                  surface_handle,
                                  synthetic_begin_frame_source,
                                  gpu_memory_buffer_manager,
-                                 GL_TEXTURE_2D,
                                  buffer_format) {}
 
 GLOutputSurfaceBufferQueueAndroid::~GLOutputSurfaceBufferQueueAndroid() =
diff --git a/components/viz/service/display_embedder/gl_output_surface_mac.cc b/components/viz/service/display_embedder/gl_output_surface_mac.cc
index ac7a84a..f05a8db 100644
--- a/components/viz/service/display_embedder/gl_output_surface_mac.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_mac.cc
@@ -18,7 +18,6 @@
                                  surface_handle,
                                  synthetic_begin_frame_source,
                                  gpu_memory_buffer_manager,
-                                 GL_TEXTURE_RECTANGLE_ARB,
                                  gfx::BufferFormat::RGBA_8888),
       overlay_validator_(
           new CompositorOverlayCandidateValidatorMac(!allow_overlays)) {}
diff --git a/components/viz/service/display_embedder/gl_output_surface_ozone.cc b/components/viz/service/display_embedder/gl_output_surface_ozone.cc
index b473f78..98903b1a 100644
--- a/components/viz/service/display_embedder/gl_output_surface_ozone.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_ozone.cc
@@ -12,13 +12,11 @@
     scoped_refptr<VizProcessContextProvider> context_provider,
     gpu::SurfaceHandle surface_handle,
     SyntheticBeginFrameSource* synthetic_begin_frame_source,
-    gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
-    uint32_t target)
+    gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager)
     : GLOutputSurfaceBufferQueue(context_provider,
                                  surface_handle,
                                  synthetic_begin_frame_source,
                                  gpu_memory_buffer_manager,
-                                 target,
                                  display::DisplaySnapshot::PrimaryFormat()) {}
 
 GLOutputSurfaceOzone::~GLOutputSurfaceOzone() = default;
diff --git a/components/viz/service/display_embedder/gl_output_surface_ozone.h b/components/viz/service/display_embedder/gl_output_surface_ozone.h
index e4b59ccc..683051f 100644
--- a/components/viz/service/display_embedder/gl_output_surface_ozone.h
+++ b/components/viz/service/display_embedder/gl_output_surface_ozone.h
@@ -15,8 +15,7 @@
       scoped_refptr<VizProcessContextProvider> context_provider,
       gpu::SurfaceHandle surface_handle,
       SyntheticBeginFrameSource* synthetic_begin_frame_source,
-      gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
-      uint32_t target);
+      gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager);
   ~GLOutputSurfaceOzone() override;
 
   // OutputSurface implementation.
diff --git a/components/viz/service/display_embedder/gpu_display_provider.cc b/components/viz/service/display_embedder/gpu_display_provider.cc
index 2641e6ec..a3a68265 100644
--- a/components/viz/service/display_embedder/gpu_display_provider.cc
+++ b/components/viz/service/display_embedder/gpu_display_provider.cc
@@ -213,8 +213,7 @@
 #if defined(USE_OZONE)
       output_surface = std::make_unique<GLOutputSurfaceOzone>(
           std::move(context_provider), surface_handle,
-          synthetic_begin_frame_source, gpu_memory_buffer_manager_.get(),
-          GL_TEXTURE_2D);
+          synthetic_begin_frame_source, gpu_memory_buffer_manager_.get());
 #elif defined(OS_MACOSX)
       output_surface = std::make_unique<GLOutputSurfaceMac>(
           std::move(context_provider), surface_handle,
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 55bc1706..fa16839af 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -122,22 +122,9 @@
   sk_sp<SkImage> MakePromiseSkImage(SkiaOutputSurfaceImpl* impl) {
     SkColorType color_type = ResourceFormatToClosestSkColorType(
         true /* gpu_compositing */, resource_format_);
-    GrBackendFormat backend_format;
-    if (!impl->is_using_vulkan_) {
-      // Convert internal format from GLES2 to platform GL.
-      const auto* version_info = impl->impl_on_gpu_->gl_version_info();
-      unsigned int texture_storage_format =
-          TextureStorageFormat(resource_format_);
-      backend_format = GrBackendFormat::MakeGL(
-          gl::GetInternalFormat(version_info, texture_storage_format),
-          render_pass_id_ ? GL_TEXTURE_2D : mailbox_holder_.texture_target);
-    } else {
-#if BUILDFLAG(ENABLE_VULKAN)
-      backend_format = GrBackendFormat::MakeVk(ToVkFormat(resource_format_));
-#else
-      NOTREACHED();
-#endif
-    }
+    GrBackendFormat backend_format = impl->GetGrBackendFormatForTexture(
+        resource_format_,
+        render_pass_id_ ? GL_TEXTURE_2D : mailbox_holder_.texture_target);
     return impl->recorder_->makePromiseTexture(
         backend_format, size_.width(), size_.height(), mipmap_,
         kTopLeft_GrSurfaceOrigin /* origin */, color_type, alpha_type_,
@@ -223,12 +210,11 @@
     // The ownership of the contexts will be passed into
     // makeYUVAPromiseTexture(). The PromiseTextureHelper::Done will always be
     // called. It will delete contexts.
-    const auto process_planar = [&](size_t i, ResourceFormat resource_format,
-                                    GLenum gl_format) {
+    const auto process_planar = [&](size_t i, ResourceFormat resource_format) {
       auto& metadata = metadatas[i];
       metadata.resource_format = resource_format;
-      formats[i] = GrBackendFormat::MakeGL(
-          gl_format, metadata.mailbox_holder.texture_target);
+      formats[i] = impl->GetGrBackendFormatForTexture(
+          resource_format, metadata.mailbox_holder.texture_target);
       yuva_sizes[i].set(metadata.size.width(), metadata.size.height());
       contexts[i] = new PromiseTextureHelper(
           impl->impl_on_gpu_->weak_ptr(), metadata.size,
@@ -237,35 +223,35 @@
     };
 
     if (is_i420) {
-      process_planar(0, RED_8, GL_R8);
+      process_planar(0, RED_8);
       indices[SkYUVAIndex::kY_Index].fIndex = 0;
       indices[SkYUVAIndex::kY_Index].fChannel = SkColorChannel::kR;
 
-      process_planar(1, RED_8, GL_R8);
+      process_planar(1, RED_8);
       indices[SkYUVAIndex::kU_Index].fIndex = 1;
       indices[SkYUVAIndex::kU_Index].fChannel = SkColorChannel::kR;
 
-      process_planar(2, RED_8, GL_R8);
+      process_planar(2, RED_8);
       indices[SkYUVAIndex::kV_Index].fIndex = 2;
       indices[SkYUVAIndex::kV_Index].fChannel = SkColorChannel::kR;
       if (has_alpha) {
-        process_planar(3, RED_8, GL_R8);
+        process_planar(3, RED_8);
         indices[SkYUVAIndex::kA_Index].fIndex = 3;
         indices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR;
       }
     } else {
-      process_planar(0, RED_8, GL_R8);
+      process_planar(0, RED_8);
       indices[SkYUVAIndex::kY_Index].fIndex = 0;
       indices[SkYUVAIndex::kY_Index].fChannel = SkColorChannel::kR;
 
-      process_planar(1, RG_88, GL_RG8);
+      process_planar(1, RG_88);
       indices[SkYUVAIndex::kU_Index].fIndex = 1;
       indices[SkYUVAIndex::kU_Index].fChannel = SkColorChannel::kR;
 
       indices[SkYUVAIndex::kV_Index].fIndex = 1;
       indices[SkYUVAIndex::kV_Index].fChannel = SkColorChannel::kG;
       if (has_alpha) {
-        process_planar(2, RED_8, GL_R8);
+        process_planar(2, RED_8);
         indices[SkYUVAIndex::kA_Index].fIndex = 2;
         indices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR;
       }
@@ -798,4 +784,24 @@
   }
 }
 
+GrBackendFormat SkiaOutputSurfaceImpl::GetGrBackendFormatForTexture(
+    ResourceFormat resource_format,
+    uint32_t gl_texture_target) {
+  if (!is_using_vulkan_) {
+    // Convert internal format from GLES2 to platform GL.
+    const auto* version_info = impl_on_gpu_->gl_version_info();
+    unsigned int texture_storage_format = TextureStorageFormat(resource_format);
+    return GrBackendFormat::MakeGL(
+        gl::GetInternalFormat(version_info, texture_storage_format),
+        gl_texture_target);
+  } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+    return GrBackendFormat::MakeVk(ToVkFormat(resource_format));
+#else
+    NOTREACHED();
+    return GrBackendFormat();
+#endif
+  }
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index 40b48c3b..a2deed0e 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -128,6 +128,8 @@
   void ContextLost();
   void ScheduleGpuTask(base::OnceClosure callback,
                        std::vector<gpu::SyncToken> sync_tokens);
+  GrBackendFormat GetGrBackendFormatForTexture(ResourceFormat resource_format,
+                                               uint32_t gl_texture_target);
 
   uint64_t sync_fence_release_ = 0;
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index cbf1b1d..4994ea7 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -82,6 +82,7 @@
     "//content/browser/devtools:devtools_background_services_proto",
     "//content/browser/devtools:protocol_sources",
     "//content/browser/dom_storage:local_storage_proto",
+    "//content/browser/indexed_db/scopes:scopes_metadata_proto",
     "//content/browser/notifications:notification_proto",
     "//content/browser/payments:payment_app_proto",
     "//content/browser/process_internals:mojo_bindings",
@@ -1045,6 +1046,8 @@
     "indexed_db/list_set.h",
     "indexed_db/scopes/disjoint_range_lock_manager.cc",
     "indexed_db/scopes/disjoint_range_lock_manager.h",
+    "indexed_db/scopes/leveldb_scopes_coding.cc",
+    "indexed_db/scopes/leveldb_scopes_coding.h",
     "indexed_db/scopes/leveldb_state.cc",
     "indexed_db/scopes/leveldb_state.h",
     "indexed_db/scopes/scope_lock.cc",
@@ -2011,6 +2014,8 @@
     sources += [
       "gpu/ca_transaction_gpu_coordinator.cc",
       "gpu/ca_transaction_gpu_coordinator.h",
+      "media/now_playing_info_center_notifier.cc",
+      "media/now_playing_info_center_notifier.h",
       "sandbox_support_mac_impl.h",
       "sandbox_support_mac_impl.mm",
       "web_contents/web_contents_ns_view_bridge.h",
diff --git a/content/browser/android/java/gin_java_script_to_java_types_coercion.cc b/content/browser/android/java/gin_java_script_to_java_types_coercion.cc
index dabd66b..84fd5489 100644
--- a/content/browser/android/java/gin_java_script_to_java_types_coercion.cc
+++ b/content/browser/android/java/gin_java_script_to_java_types_coercion.cc
@@ -722,8 +722,14 @@
     case base::Value::Type::BINARY:
       return CoerceGinJavaBridgeValueToJavaValue(
           env, value, target_type, coerce_to_string, object_refs, error);
+    // TODO(crbug.com/859477): Remove after root cause is found.
+    case base::Value::Type::DEAD:
+      CHECK(false);
+      return jvalue();
   }
-  NOTREACHED();
+
+  // TODO(crbug.com/859477): Revert to NOTREACHED() after root cause is found.
+  CHECK(false);
   return jvalue();
 }
 
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index 2bb0d19..1801dee 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -363,6 +363,12 @@
                             PROCESS_TYPE_MAX);
 }
 
+#if defined(OS_ANDROID)
+void BrowserChildProcessHostImpl::EnableWarmUpConnection() {
+  can_use_warm_up_connection_ = true;
+}
+#endif
+
 ChildProcessTerminationInfo BrowserChildProcessHostImpl::GetTerminationInfo(
     bool known_dead) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -596,6 +602,12 @@
   delete delegate_;  // Will delete us
 }
 
+#if defined(OS_ANDROID)
+bool BrowserChildProcessHostImpl::CanUseWarmUpConnection() {
+  return can_use_warm_up_connection_;
+}
+#endif
+
 void BrowserChildProcessHostImpl::OnProcessLaunched() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
diff --git a/content/browser/browser_child_process_host_impl.h b/content/browser/browser_child_process_host_impl.h
index 5c07d1b3..4909df74 100644
--- a/content/browser/browser_child_process_host_impl.h
+++ b/content/browser/browser_child_process_host_impl.h
@@ -115,6 +115,10 @@
 
   static void HistogramBadMessageTerminated(ProcessType process_type);
 
+#if defined(OS_ANDROID)
+  void EnableWarmUpConnection();
+#endif
+
   BrowserChildProcessHostDelegate* delegate() const { return delegate_; }
 
   ChildConnection* child_connection() const {
@@ -148,6 +152,9 @@
   // ChildProcessLauncher::Client implementation.
   void OnProcessLaunched() override;
   void OnProcessLaunchFailed(int error_code) override;
+#if defined(OS_ANDROID)
+  bool CanUseWarmUpConnection() override;
+#endif
 
   // Returns true if the process has successfully launched. Must only be called
   // on the IO thread.
@@ -191,6 +198,12 @@
   bool is_channel_connected_;
   bool notify_child_disconnected_;
 
+#if defined(OS_ANDROID)
+  // whether the child process can use pre-warmed up connection for better
+  // performance.
+  bool can_use_warm_up_connection_ = false;
+#endif
+
   base::WeakPtrFactory<BrowserChildProcessHostImpl> weak_factory_;
 };
 
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index f48d0b3..e3086c3 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -23,6 +23,12 @@
 
 using internal::ChildProcessLauncherHelper;
 
+#if defined(OS_ANDROID)
+bool ChildProcessLauncher::Client::CanUseWarmUpConnection() {
+  return true;
+}
+#endif
+
 ChildProcessLauncher::ChildProcessLauncher(
     std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
     std::unique_ptr<base::CommandLine> command_line,
@@ -48,6 +54,9 @@
   helper_ = new ChildProcessLauncherHelper(
       child_process_id, client_thread_id_, std::move(command_line),
       std::move(delegate), weak_factory_.GetWeakPtr(), terminate_on_shutdown,
+#if defined(OS_ANDROID)
+      client_->CanUseWarmUpConnection(),
+#endif
       std::move(mojo_invitation), process_error_callback);
   helper_->StartLaunchOnClientThread();
 }
diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h
index 407bfc4..d90446a 100644
--- a/content/browser/child_process_launcher.h
+++ b/content/browser/child_process_launcher.h
@@ -142,6 +142,10 @@
 
     virtual void OnProcessLaunchFailed(int error_code) {}
 
+#if defined(OS_ANDROID)
+    // Whether the process can use pre-warmed up connection.
+    virtual bool CanUseWarmUpConnection();
+#endif
    protected:
     virtual ~Client() {}
   };
diff --git a/content/browser/child_process_launcher_browsertest.cc b/content/browser/child_process_launcher_browsertest.cc
index 2949aacb..c9dbb380 100644
--- a/content/browser/child_process_launcher_browsertest.cc
+++ b/content/browser/child_process_launcher_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/command_line.h"
+#include "build/build_config.h"
 #include "content/browser/child_process_launcher.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/public/browser/navigation_entry.h"
@@ -33,6 +34,10 @@
     client_->OnProcessLaunchFailed(error_code);
   }
 
+#if defined(OS_ANDROID)
+  bool CanUseWarmUpConnection() override { return true; }
+#endif
+
   content::ChildProcessLauncher::Client* client_;
   bool simulate_failure_;
 };
diff --git a/content/browser/child_process_launcher_helper.cc b/content/browser/child_process_launcher_helper.cc
index c931d48..8cbede7e 100644
--- a/content/browser/child_process_launcher_helper.cc
+++ b/content/browser/child_process_launcher_helper.cc
@@ -71,6 +71,9 @@
     std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
     const base::WeakPtr<ChildProcessLauncher>& child_process_launcher,
     bool terminate_on_shutdown,
+#if defined(OS_ANDROID)
+    bool can_use_warm_up_connection,
+#endif
     mojo::OutgoingInvitation mojo_invitation,
     const mojo::ProcessErrorCallback& process_error_callback)
     : child_process_id_(child_process_id),
@@ -80,7 +83,13 @@
       child_process_launcher_(child_process_launcher),
       terminate_on_shutdown_(terminate_on_shutdown),
       mojo_invitation_(std::move(mojo_invitation)),
-      process_error_callback_(process_error_callback) {}
+      process_error_callback_(process_error_callback)
+#if defined(OS_ANDROID)
+      ,
+      can_use_warm_up_connection_(can_use_warm_up_connection)
+#endif
+{
+}
 
 ChildProcessLauncherHelper::~ChildProcessLauncherHelper() = default;
 
@@ -118,6 +127,9 @@
   if (BeforeLaunchOnLauncherThread(*files_to_register, &options)) {
     process =
         LaunchProcessOnLauncherThread(options, std::move(files_to_register),
+#if defined(OS_ANDROID)
+                                      can_use_warm_up_connection_,
+#endif
                                       &is_synchronous_launch, &launch_result);
 
     AfterLaunchOnLauncherThread(process, options);
diff --git a/content/browser/child_process_launcher_helper.h b/content/browser/child_process_launcher_helper.h
index 301b8443..feb5794 100644
--- a/content/browser/child_process_launcher_helper.h
+++ b/content/browser/child_process_launcher_helper.h
@@ -98,6 +98,9 @@
       std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
       const base::WeakPtr<ChildProcessLauncher>& child_process_launcher,
       bool terminate_on_shutdown,
+#if defined(OS_ANDROID)
+      bool is_pre_warmup_required,
+#endif
       mojo::OutgoingInvitation mojo_invitation,
       const mojo::ProcessErrorCallback& process_error_callback);
 
@@ -138,6 +141,9 @@
   ChildProcessLauncherHelper::Process LaunchProcessOnLauncherThread(
       const base::LaunchOptions& options,
       std::unique_ptr<FileMappedForLaunch> files_to_register,
+#if defined(OS_ANDROID)
+      bool is_pre_warmup_required,
+#endif
       bool* is_synchronous_launch,
       int* launch_result);
 
@@ -247,6 +253,8 @@
 #if defined(OS_ANDROID)
   base::android::ScopedJavaGlobalRef<jobject> java_peer_;
   bool java_peer_avaiable_on_client_thread_ = false;
+  // Whether the process can use warmed up connection.
+  bool can_use_warm_up_connection_;
 #endif
 
 #if defined(OS_FUCHSIA)
diff --git a/content/browser/child_process_launcher_helper_android.cc b/content/browser/child_process_launcher_helper_android.cc
index c9c2f22..0b44425 100644
--- a/content/browser/child_process_launcher_helper_android.cc
+++ b/content/browser/child_process_launcher_helper_android.cc
@@ -98,6 +98,7 @@
 ChildProcessLauncherHelper::LaunchProcessOnLauncherThread(
     const base::LaunchOptions& options,
     std::unique_ptr<PosixFileDescriptorInfo> files_to_register,
+    bool can_use_warm_up_connection,
     bool* is_synchronous_launch,
     int* launch_result) {
   *is_synchronous_launch = false;
@@ -136,7 +137,8 @@
   }
 
   java_peer_.Reset(Java_ChildProcessLauncherHelperImpl_createAndStart(
-      env, reinterpret_cast<intptr_t>(this), j_argv, j_file_infos));
+      env, reinterpret_cast<intptr_t>(this), j_argv, j_file_infos,
+      can_use_warm_up_connection));
   AddRef();  // Balanced by OnChildProcessStarted.
   base::PostTaskWithTraits(
       FROM_HERE, {client_thread_id_},
diff --git a/content/browser/compositor/gpu_output_surface_mac.cc b/content/browser/compositor/gpu_output_surface_mac.cc
index 0e9607b..f6024e3e 100644
--- a/content/browser/compositor/gpu_output_surface_mac.cc
+++ b/content/browser/compositor/gpu_output_surface_mac.cc
@@ -24,7 +24,6 @@
           surface_handle,
           update_vsync_parameters_callback,
           std::move(overlay_candidate_validator),
-          GL_TEXTURE_RECTANGLE_ARB,
           gfx::BufferFormat::RGBA_8888,
           gpu_memory_buffer_manager) {}
 
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 62df11f..ff4e310 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -480,7 +480,7 @@
           std::make_unique<GpuSurfacelessBrowserCompositorOutputSurface>(
               context_provider, data->surface_handle, std::move(vsync_callback),
               CreateOverlayCandidateValidator(compositor->widget()),
-              GL_TEXTURE_2D, display::DisplaySnapshot::PrimaryFormat(),
+              display::DisplaySnapshot::PrimaryFormat(),
               GetGpuMemoryBufferManager());
       display_output_surface = std::move(gpu_output_surface);
 #endif
diff --git a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc
index 41845d0b..ad0a788 100644
--- a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc
@@ -25,7 +25,6 @@
         const UpdateVSyncParametersCallback& update_vsync_parameters_callback,
         std::unique_ptr<viz::CompositorOverlayCandidateValidator>
             overlay_candidate_validator,
-        unsigned int target,
         gfx::BufferFormat format,
         gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager)
     : GpuBrowserCompositorOutputSurface(std::move(context),
@@ -48,9 +47,9 @@
   // implementation.
   capabilities_.max_frames_pending = 2;
 
-  buffer_queue_.reset(
-      new viz::BufferQueue(context_provider_->ContextGL(), target, format,
-                           gpu_memory_buffer_manager_, surface_handle));
+  buffer_queue_.reset(new viz::BufferQueue(
+      context_provider_->ContextGL(), format, gpu_memory_buffer_manager_,
+      surface_handle, context_provider_->ContextCapabilities()));
   buffer_queue_->Initialize();
 }
 
diff --git a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h
index 6effcff..b29ee9ef 100644
--- a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h
+++ b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h
@@ -30,7 +30,6 @@
       const UpdateVSyncParametersCallback& update_vsync_parameters_callback,
       std::unique_ptr<viz::CompositorOverlayCandidateValidator>
           overlay_candidate_validator,
-      unsigned int target,
       gfx::BufferFormat format,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager);
   ~GpuSurfacelessBrowserCompositorOutputSurface() override;
diff --git a/content/browser/devtools/devtools_http_handler.cc b/content/browser/devtools/devtools_http_handler.cc
index 3a1bc87..94892dd 100644
--- a/content/browser/devtools/devtools_http_handler.cc
+++ b/content/browser/devtools/devtools_http_handler.cc
@@ -150,8 +150,7 @@
                      const net::HttpServerRequestInfo& info) override;
   void OnWebSocketRequest(int connection_id,
                           const net::HttpServerRequestInfo& info) override;
-  void OnWebSocketMessage(int connection_id,
-                          const std::string& data) override;
+  void OnWebSocketMessage(int connection_id, std::string data) override;
   void OnClose(int connection_id) override;
 
   base::WeakPtr<DevToolsHttpHandler> handler_;
@@ -485,12 +484,11 @@
                      connection_id, request));
 }
 
-void ServerWrapper::OnWebSocketMessage(int connection_id,
-                                       const std::string& data) {
+void ServerWrapper::OnWebSocketMessage(int connection_id, std::string data) {
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
       base::BindOnce(&DevToolsHttpHandler::OnWebSocketMessage, handler_,
-                     connection_id, data));
+                     connection_id, std::move(data)));
 }
 
 void ServerWrapper::OnClose(int connection_id) {
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index de46b31..580c8e4 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -7737,10 +7737,12 @@
             controller.GetLastCommittedEntry()->GetVirtualURL());
 }
 
-// Verifies that unsafe redirects to javascript: or other URLs create an error
-// page and don't make a spoof possible. See https://crbug.com/935175.
+// Verifies that unsafe redirects to javascript: URLs are canceled and don't
+// make a spoof possible. Ideally they would create an error page, but some
+// extensions rely on them being silently blocked. See https://crbug.com/935175
+// and https://cbug.com/941653.
 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
-                       UnsafeRedirectCreatesErrorPage) {
+                       JavascriptRedirectSilentlyCanceled) {
   NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
       shell()->web_contents()->GetController());
 
@@ -7748,13 +7750,17 @@
   EXPECT_TRUE(NavigateToURL(shell(), start_url));
   EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
 
-  // Navigating to URLs with unsafe redirects should create an error page so
-  // that the pending URL is not left in the address bar.
+  // Navigating to a URL that redirects to a javascript: URL doesn't create an
+  // error page; the navigation is simply ignored. Check the pending URL is not
+  // left in the address bar.
   GURL redirect_to_unsafe_url(
       embedded_test_server()->GetURL("/server-redirect?javascript:Hello!"));
   EXPECT_FALSE(NavigateToURL(shell(), redirect_to_unsafe_url));
-  EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
-  EXPECT_EQ(PAGE_TYPE_ERROR, controller.GetLastCommittedEntry()->GetPageType());
+  EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
+  EXPECT_EQ(PAGE_TYPE_NORMAL,
+            controller.GetLastCommittedEntry()->GetPageType());
+  EXPECT_EQ(controller.GetVisibleEntry(), controller.GetLastCommittedEntry());
+  EXPECT_EQ(start_url, controller.GetVisibleEntry()->GetURL());
 }
 
 // Verifies that redirecting to a blocked URL and going back does not allow a
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index f4c05ba..81219960 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -1295,19 +1295,19 @@
 
   const GURL url1("http://foo1");
 
-  auto navigation = NavigationSimulator::CreateBrowserInitiated(
-      url1, RenderViewHostTestHarness::web_contents());
-  navigation->Start();
+  controller.LoadURL(
+      url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
+  int entry_id = controller.GetPendingEntry()->GetUniqueID();
   EXPECT_EQ(0U, navigation_entry_changed_counter_);
   EXPECT_EQ(0U, navigation_list_pruned_counter_);
-  navigation->Commit();
+  main_test_rfh()->PrepareForCommit();
+  main_test_rfh()->SendNavigate(entry_id, true, url1);
   EXPECT_EQ(1U, navigation_entry_committed_counter_);
   navigation_entry_committed_counter_ = 0;
   ASSERT_TRUE(controller.GetVisibleEntry());
+  entry_id = controller.GetLastCommittedEntry()->GetUniqueID();
 
   controller.Reload(ReloadType::NORMAL, true);
-  navigation = NavigationSimulator::CreateFromPending(
-      RenderViewHostTestHarness::web_contents());
   EXPECT_EQ(0U, navigation_entry_changed_counter_);
   EXPECT_EQ(0U, navigation_list_pruned_counter_);
 
@@ -1323,7 +1323,8 @@
   EXPECT_FALSE(controller.CanGoBack());
   EXPECT_FALSE(controller.CanGoForward());
 
-  navigation->Commit();
+  main_test_rfh()->PrepareForCommit();
+  main_test_rfh()->SendNavigate(entry_id, false, url1);
   EXPECT_EQ(1U, navigation_entry_committed_counter_);
   navigation_entry_committed_counter_ = 0;
 
@@ -1411,22 +1412,32 @@
   EXPECT_EQ(entry1, entry2);
 }
 
+namespace {
+void SetOriginalURL(const GURL& url,
+                    FrameHostMsg_DidCommitProvisionalLoad_Params* params) {
+  params->original_request_url = url;
+}
+}
+
 TEST_F(NavigationControllerTest, ReloadOriginalRequestURL) {
   NavigationControllerImpl& controller = controller_impl();
 
   const GURL original_url("http://foo1");
   const GURL final_url("http://foo2");
+  auto set_original_url_callback = base::Bind(SetOriginalURL, original_url);
 
   // Load up the original URL, but get redirected.
-  auto navigation = NavigationSimulator::CreateBrowserInitiated(
-      original_url, RenderViewHostTestHarness::web_contents());
-  navigation->Start();
+  controller.LoadURL(
+      original_url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
+  int entry_id = controller.GetPendingEntry()->GetUniqueID();
   EXPECT_EQ(0U, navigation_entry_changed_counter_);
   EXPECT_EQ(0U, navigation_list_pruned_counter_);
-  navigation->Redirect(final_url);
-  navigation->Commit();
+  main_test_rfh()->PrepareForCommitWithServerRedirect(final_url);
+  main_test_rfh()->SendNavigateWithModificationCallback(
+      entry_id, true, final_url, std::move(set_original_url_callback));
   EXPECT_EQ(1U, navigation_entry_committed_counter_);
   navigation_entry_committed_counter_ = 0;
+  entry_id = controller.GetLastCommittedEntry()->GetUniqueID();
 
   // The NavigationEntry should save both the original URL and the final
   // redirected URL.
@@ -1450,10 +1461,8 @@
   EXPECT_FALSE(controller.CanGoForward());
 
   // Send that the navigation has proceeded; say it got redirected again.
-  navigation = NavigationSimulator::CreateFromPending(
-      RenderViewHostTestHarness::web_contents());
-  navigation->Redirect(final_url);
-  navigation->Commit();
+  main_test_rfh()->PrepareForCommitWithServerRedirect(final_url);
+  main_test_rfh()->SendNavigate(entry_id, false, final_url);
   EXPECT_EQ(1U, navigation_entry_committed_counter_);
   navigation_entry_committed_counter_ = 0;
 
diff --git a/content/browser/frame_host/navigation_handle_impl_browsertest.cc b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
index 5d212d4c..fe1d6ea 100644
--- a/content/browser/frame_host/navigation_handle_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
@@ -1906,9 +1906,14 @@
     NavigationLogger logger(shell()->web_contents());
 
     // Try to navigate to the url. The navigation should be canceled and the
-    // NavigationHandle should have the right error code.
+    // NavigationHandle should have the right error code.  Note that javascript
+    // URLS use ERR_ABORTED rather than ERR_UNSAFE_REDIRECT due to
+    // https://crbug.com/941653.
     EXPECT_FALSE(NavigateToURL(shell(), redirecting_url));
-    EXPECT_EQ(net::ERR_UNSAFE_REDIRECT, observer.net_error_code());
+    int expected_err_code = test_url.SchemeIs("javascript")
+                                ? net::ERR_ABORTED
+                                : net::ERR_UNSAFE_REDIRECT;
+    EXPECT_EQ(expected_err_code, observer.net_error_code());
 
     // Both WebContentsObserver::{DidStartNavigation, DidFinishNavigation}
     // are called, but no WebContentsObserver::DidRedirectNavigation.
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 4251538..d525637 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -908,9 +908,12 @@
           redirect_info.new_url)) {
     DVLOG(1) << "Denied redirect for "
              << redirect_info.new_url.possibly_invalid_spec();
-    // Show an error page rather than leaving the previous page in place.
+    // TODO(arthursonzogni): Redirect to a javascript URL should display an
+    // error page with the net::ERR_UNSAFE_REDIRECT error code. Instead, the
+    // browser simply ignores the navigation, because some extensions use this
+    // edge case to silently cancel navigations. See https://crbug.com/941653.
     OnRequestFailedInternal(
-        network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT),
+        network::URLLoaderCompletionStatus(net::ERR_ABORTED),
         false /* skip_throttles */, base::nullopt /* error_page_content */,
         false /* collapse_frame */);
     // DO NOT ADD CODE after this. The previous call to OnRequestFailedInternal
@@ -927,9 +930,11 @@
           redirect_info.new_url)) {
     DVLOG(1) << "Denied unauthorized redirect for "
              << redirect_info.new_url.possibly_invalid_spec();
-    // Show an error page rather than leaving the previous page in place.
+    // TODO(arthursonzogni): This case uses ERR_ABORTED to be consistent with
+    // the javascript URL redirect case above, though ideally it would use
+    // net::ERR_UNSAFE_REDIRECT and an error page. See https://crbug.com/941653.
     OnRequestFailedInternal(
-        network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT),
+        network::URLLoaderCompletionStatus(net::ERR_ABORTED),
         false /* skip_throttles */, base::nullopt /* error_page_content */,
         false /* collapse_frame */);
     // DO NOT ADD CODE after this. The previous call to OnRequestFailedInternal
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 74ecd97..cf701a9a 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1014,6 +1014,14 @@
   return *overlay_routing_token_;
 }
 
+void RenderFrameHostImpl::DidCommitNavigationForTesting(
+    NavigationRequest* navigation_request,
+    std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params,
+    mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params) {
+  DidCommitNavigation(navigation_request, std::move(params),
+                      std::move(interface_params));
+}
+
 void RenderFrameHostImpl::AudioContextPlaybackStarted(int audio_context_id) {
   delegate_->AudioContextPlaybackStarted(this, audio_context_id);
 }
@@ -2129,7 +2137,7 @@
     mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params) {
   if (MaybeInterceptCommitCallback(nullptr, validated_params.get(),
                                    &interface_params)) {
-    DidCommitNavigation(std::move(navigation_request_),
+    DidCommitNavigation(nullptr /* committing_navigation_request */,
                         std::move(validated_params),
                         std::move(interface_params));
   }
@@ -2142,19 +2150,12 @@
     mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params) {
   DCHECK(committing_navigation_request);
   committing_navigation_request->IgnoreCommitInterfaceDisconnection();
-  if (!MaybeInterceptCommitCallback(committing_navigation_request,
-                                    validated_params.get(),
-                                    &interface_params)) {
-    return;
+  if (MaybeInterceptCommitCallback(committing_navigation_request,
+                                   validated_params.get(), &interface_params)) {
+    DidCommitNavigation(committing_navigation_request,
+                        std::move(validated_params),
+                        std::move(interface_params));
   }
-  auto request = navigation_requests_.find(committing_navigation_request);
-
-  // The committing request should be in the map of NavigationRequests for
-  // this RenderFrameHost.
-  CHECK(request != navigation_requests_.end());
-
-  DidCommitNavigation(std::move(request->second), std::move(validated_params),
-                      std::move(interface_params));
 }
 
 void RenderFrameHostImpl::DidCommitSameDocumentNavigation(
@@ -2180,14 +2181,13 @@
                "RenderFrameHostImpl::DidCommitSameDocumentNavigation",
                "frame_tree_node", frame_tree_node_->frame_tree_node_id(), "url",
                validated_params->url.possibly_invalid_spec());
-  // TODO(clamy): We shouldn't be trusting the renderer to tell us whether this
-  // was browser-initiated. Use an UnguessableToken to do request matching
-  // instead of using nav_entry_id.
-  bool is_browser_initiated = (validated_params->nav_entry_id != 0);
-  if (!DidCommitNavigationInternal(
-          is_browser_initiated ? std::move(same_document_navigation_request_)
-                               : nullptr,
-          validated_params.get(), true /* is_same_document_navigation*/)) {
+
+  // TODO(ahemery): We also create a NavigationRequest for browser initiated
+  // same document navigations, so implement the passing of this request to
+  // DidCommitNavigationInternal.
+  if (!DidCommitNavigationInternal(nullptr /* navigation_request */,
+                                   validated_params.get(),
+                                   true /* is_same_document_navigation*/)) {
     return;
   }
 
@@ -5789,38 +5789,115 @@
 }
 
 std::unique_ptr<NavigationRequest>
-RenderFrameHostImpl::CreateNavigationRequestForCommit(
-    const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
-    bool is_same_document,
-    NavigationEntryImpl* entry_for_request) {
-  bool is_renderer_initiated =
-      entry_for_request ? entry_for_request->is_renderer_initiated() : true;
-  return NavigationRequest::CreateForCommit(
-      frame_tree_node_, this, entry_for_request, params, is_renderer_initiated,
-      is_same_document);
-}
+RenderFrameHostImpl::TakeNavigationRequestForSameDocumentCommit(
+    const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
+  bool is_browser_initiated = (params.nav_entry_id != 0);
 
-bool RenderFrameHostImpl::NavigationRequestWasIntendedForPendingEntry(
-    NavigationRequest* request,
-    const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
-    bool same_document) {
+  // A NavigationRequest is created for browser-initiated same-document
+  // navigation. Try to take it if it's still available and matches the
+  // current navigation.
+  if (is_browser_initiated && same_document_navigation_request_ &&
+      same_document_navigation_request_->common_params().url == params.url) {
+    return std::move(same_document_navigation_request_);
+  }
+
+  // No existing NavigationRequest has been found. Create a new one, but don't
+  // reset any NavigationRequest tracking an ongoing navigation, since this may
+  // lead to the cancellation of the navigation.
+  // First, determine if the navigation corresponds to the pending navigation
+  // entry. This is the case if the NavigationRequest for a browser-initiated
+  // same-document navigation was erased due to a race condition.
+  // TODO(ahemery): Remove when the full mojo interface is in place.
+  // (https://bugs.chromium.org/p/chromium/issues/detail?id=784904)
+  bool is_renderer_initiated = true;
   NavigationEntryImpl* pending_entry = NavigationEntryImpl::FromNavigationEntry(
       frame_tree_node()->navigator()->GetController()->GetPendingEntry());
-  if (!pending_entry)
-    return false;
-  if (request->nav_entry_id() != pending_entry->GetUniqueID())
-    return false;
-  if (!same_document) {
-    // Make sure that the pending entry was really loaded via
-    // LoadDataWithBaseURL and that it matches this handle.
+  if (pending_entry && pending_entry->GetUniqueID() == params.nav_entry_id) {
+    is_renderer_initiated = pending_entry->is_renderer_initiated();
+  } else {
+    // Don't reuse the pending entry if it doesn't match.
+    pending_entry = nullptr;
+  }
+
+  return NavigationRequest::CreateForCommit(
+      frame_tree_node_, this, pending_entry, params, is_renderer_initiated,
+      true /* was_within_same_document */);
+}
+
+std::unique_ptr<NavigationRequest>
+RenderFrameHostImpl::TakeNavigationRequestForCommit(
+    const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
+  // TODO(ahemery): Once we have IsPerNavigationMojoInterfaceEnabled() always
+  // true, it becomes obsolete to match the NavigationRequest since we are
+  // sure it is the correct one. However using the same request even though
+  // url might have changed ("" becomes "about:blank", etc.) requires some
+  // updating.
+
+  // Determine if the current NavigationRequest can be used.
+  NavigationHandleImpl* navigation_handle =
+      navigation_request_ ? navigation_request_->navigation_handle() : nullptr;
+
+  // TODO(lukasza, clamy): https://crbug.com/784904: Match commit IPC to proper
+  // NavigationHandle without requiring URLs to match.
+  if (navigation_handle && navigation_handle->GetURL() == params.url) {
+    return std::move(navigation_request_);
+  }
+
+  // At this point we know that the right/matching |navigation_request_| has
+  // already been found based on navigation id look-up performed by
+  // RFHI::OnCrossDocumentCommitProcessed.  OTOH, we cannot use its
+  // NavigationHandle, because it has a mismatched URL (which would cause
+  // DCHECKs - for example in NavigationHandleImpl::DidCommitNavigation).
+  //
+  // Because of the above, if the URL does not match what the NavigationHandle
+  // expects, we want to treat the commit as a new navigation.
+  // This mostly works, but there are some remaining issues here tracked
+  // by https://crbug.com/872803.
+  //
+  // The URL mismatch can happen when loading a Data navigation with
+  // LoadDataWithBaseURL.
+  // TODO(csharrison): Data navigations loaded with LoadDataWithBaseURL get
+  // reset here, because the NavigationHandle tracks the URL but the params.url
+  // tracks the data. The trick of saving the old entry ids for these
+  // navigations should go away when this is properly handled.
+  // See https://crbug.com/588317.
+  //
+  // Other cases are where URL mismatch can happen is when committing an error
+  // page - for example this can happen during CSP/frame-ancestors checks (see
+  // https://crbug.com/759184).
+
+  NavigationEntryImpl* entry_for_request = nullptr;
+  bool is_renderer_initiated = true;
+
+  // Make sure that the pending entry was really loaded via LoadDataWithBaseURL
+  // and that it matches this handle.  TODO(csharrison): The pending entry's
+  // base url should equal |params.base_url|. This is not the case for loads
+  // with invalid base urls.
+  if (navigation_handle) {
+    NavigationEntryImpl* pending_entry =
+        NavigationEntryImpl::FromNavigationEntry(
+            frame_tree_node()->navigator()->GetController()->GetPendingEntry());
+    bool pending_entry_matches_handle =
+        pending_entry && pending_entry->GetUniqueID() ==
+                             navigation_handle->pending_nav_entry_id();
     // TODO(csharrison): The pending entry's base url should equal
-    // |params.base_url|. This is not the case for loads with invalid base urls.
-    if (request->common_params().url != params.base_url ||
-        pending_entry->GetBaseURLForDataURL().is_empty()) {
-      return false;
+    // |validated_params.base_url|. This is not the case for loads with invalid
+    // base urls.
+    if (navigation_handle->GetURL() == params.base_url &&
+        pending_entry_matches_handle &&
+        !pending_entry->GetBaseURLForDataURL().is_empty()) {
+      entry_for_request = pending_entry;
+      is_renderer_initiated = pending_entry->is_renderer_initiated();
     }
   }
-  return true;
+
+  // There is no pending NavigationEntry in these cases, so pass 0 as the
+  // pending_nav_entry_id. If the previous handle was a prematurely aborted
+  // navigation loaded via LoadDataWithBaseURL, propagate the entry id.
+  return NavigationRequest::CreateForCommit(
+      frame_tree_node_, this, entry_for_request, params,
+      is_renderer_initiated /* is_renderer_initiated */,
+      false /* is_same_document */);
 }
 
 void RenderFrameHostImpl::BeforeUnloadTimeout() {
@@ -6144,60 +6221,21 @@
 }
 
 bool RenderFrameHostImpl::DidCommitNavigationInternal(
-    std::unique_ptr<NavigationRequest> navigation_request,
+    NavigationRequest* navigation_request,
     FrameHostMsg_DidCommitProvisionalLoad_Params* validated_params,
     bool is_same_document_navigation) {
   // Sanity-check the page transition for frame type.
   DCHECK_EQ(ui::PageTransitionIsMainFrame(validated_params->transition),
             !GetParent());
 
-  // Check that the committing URL matches the navigation request.
-  // TODO(clamy, nasko): Do not use the URL to make the matching check.
-  std::unique_ptr<NavigationRequest> invalid_request = nullptr;
-  if (navigation_request &&
-      navigation_request->common_params().url != validated_params->url) {
-    // At this point we know that the right/matching |navigation_request_| has
-    // already been found based on pointer look-up performed by
-    // RFHI::OnCrossDocumentCommitProcessed or the commit originated from a
-    // NavigationClient owned by the NavigationRequest. OTOH, we cannot use its
-    // NavigationHandle, because it has a mismatched URL (which would cause
-    // DCHECKs - for example in NavigationHandleImpl::DidCommitNavigation).
-    //
-    // Because of the above, if the URL does not match what the NavigationHandle
-    // expects, we want to treat the commit as a new navigation.
-    //
-    // The URL mismatch can happen when loading a Data navigation with
-    // LoadDataWithBaseURL.
-    // TODO(csharrison): Data navigations loaded with LoadDataWithBaseURL get
-    // reset here, because the NavigationHandle tracks the URL but the
-    // params.url tracks the data. The trick of saving the old entry ids for
-    // these navigations should go away when this is properly handled. See
-    // https://crbug.com/588317.
-    //
-    // Other cases are where URL mismatch can happen is when committing an error
-    // page - for example this can happen during CSP/frame-ancestors checks (see
-    // https://crbug.com/759184).
-
-    // Note: the NavigationRequest is not reset here, as this could potentially
-    // lead to the deletion of the pending NavigationEntry.
-    invalid_request = std::move(navigation_request);
-
-    // TODO(clamy): We should kill the renderer in all cases where we expect to
-    // have a NavigationRequest matching the commit URL.
+  if (navigation_request) {
+    OnCrossDocumentCommitProcessed(navigation_request,
+                                   blink::mojom::CommitResult::Ok);
   }
 
   if (!ValidateDidCommitParams(validated_params, is_same_document_navigation))
     return false;
 
-  // The previous call might have changed the committing URL. Check if the
-  // NavigationRequest still has a matching URL.
-  // TODO(clamy): We should support the URL filtering without deleting the
-  // request.
-  if (navigation_request &&
-      navigation_request->common_params().url != validated_params->url) {
-    invalid_request = std::move(navigation_request);
-  }
-
   // Set is loading to true now if it has not been set yet. This happens for
   // renderer-initiated same-document navigations. It can also happen when a
   // racy DidStopLoading IPC resets the loading state that was set to true in
@@ -6209,62 +6247,45 @@
                                        was_loading);
   }
 
-  if (navigation_request)
-    was_discarded_ = navigation_request->commit_params().was_discarded;
+  if (navigation_request_)
+    was_discarded_ = navigation_request_->commit_params().was_discarded;
 
-  // If there is no valid NavigationRequest corresponding to this commit, create
-  // one in order to properly issue DidFinishNavigation calls to
-  // WebContentsObservers.
-  if (!navigation_request) {
-    // First check if there was a request for this navigation that cannot be
-    // used due to URL mismatch. If that's the case and it corresponds to a
-    // navigation to the pending NavigationEntry, the new request should be
-    // associated with the pending NavigationEntry as well so that the pending
-    // NavigationEntry is properly committed.
-    NavigationEntryImpl* entry_for_navigation = nullptr;
-    if (invalid_request && NavigationRequestWasIntendedForPendingEntry(
-                               invalid_request.get(), *validated_params,
-                               is_same_document_navigation)) {
-      entry_for_navigation = NavigationEntryImpl::FromNavigationEntry(
-          frame_tree_node()->navigator()->GetController()->GetPendingEntry());
-    }
-
-    navigation_request = CreateNavigationRequestForCommit(
-        *validated_params, is_same_document_navigation, entry_for_navigation);
+  std::unique_ptr<NavigationRequest> committed_request;
+  if (is_same_document_navigation) {
+    committed_request =
+        TakeNavigationRequestForSameDocumentCommit(*validated_params);
+  } else {
+    committed_request = TakeNavigationRequestForCommit(*validated_params);
   }
 
-  DCHECK(navigation_request);
-  DCHECK(navigation_request->navigation_handle());
+  DCHECK(committed_request);
+  DCHECK(committed_request->navigation_handle());
 
   // Update the page transition. For subframe navigations, the renderer process
   // only gives the correct page transition at commit time.
   // TODO(clamy): We should get the correct page transition when starting the
   // request.
-  navigation_request->set_transition(validated_params->transition);
+  committed_request->set_transition(validated_params->transition);
 
-  navigation_request->set_has_user_gesture(validated_params->gesture ==
-                                           NavigationGestureUser);
+  committed_request->set_has_user_gesture(validated_params->gesture ==
+                                          NavigationGestureUser);
 
   UpdateSiteURL(validated_params->url, validated_params->url_is_unreachable);
 
   // Set the state whether this navigation is to an MHTML document, since there
   // are certain security checks that we cannot apply to subframes in MHTML
   // documents. Do not trust renderer data when determining that, rather use
-  // the |navigation_request|, which was generated and stays browser side.
+  // the |committed_request|, which was generated and stays browser side.
   is_mhtml_document_ =
-      (navigation_request->GetMimeType() == "multipart/related" ||
-       navigation_request->GetMimeType() == "message/rfc822");
+      (committed_request->GetMimeType() == "multipart/related" ||
+       committed_request->GetMimeType() == "message/rfc822");
 
   accessibility_reset_count_ = 0;
   frame_tree_node()->navigator()->DidNavigate(this, *validated_params,
-                                              std::move(navigation_request),
+                                              std::move(committed_request),
                                               is_same_document_navigation);
-
-  // TODO(clamy): We should stop having a special case for same-document
-  // navigation and just put them in the general map of NavigationRequests.
-  if (is_same_document_navigation && invalid_request)
-    same_document_navigation_request_ = std::move(invalid_request);
-
+  if (!is_same_document_navigation)
+    navigation_request_.reset();
   return true;
 }
 
@@ -6450,7 +6471,7 @@
 // Called when the renderer navigates.  For every frame loaded, we'll get this
 // notification containing parameters identifying the navigation.
 void RenderFrameHostImpl::DidCommitNavigation(
-    std::unique_ptr<NavigationRequest> committing_navigation_request,
+    NavigationRequest* committing_navigation_request,
     std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params>
         validated_params,
     mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params) {
@@ -6556,7 +6577,7 @@
     // therefore the global object is not replaced.
   }
 
-  if (!DidCommitNavigationInternal(std::move(committing_navigation_request),
+  if (!DidCommitNavigationInternal(committing_navigation_request,
                                    validated_params.get(),
                                    false /* is_same_document_navigation */)) {
     return;
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index e91feba..455fc85 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -134,7 +134,6 @@
 class GeolocationServiceImpl;
 class KeepAliveHandleFactory;
 class MediaInterfaceProxy;
-class NavigationEntryImpl;
 class NavigationHandleImpl;
 class NavigationRequest;
 class PermissionServiceContext;
@@ -836,6 +835,11 @@
   // waiting to commit in this RenderFrameHost.
   std::set<int> GetNavigationEntryIdsPendingCommit();
 
+  void DidCommitNavigationForTesting(
+      NavigationRequest* navigation_request,
+      std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params,
+      mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params);
+
   service_manager::BinderRegistry& BinderRegistryForTesting() {
     return *registry_;
   }
@@ -1338,24 +1342,12 @@
                                  bool success,
                                  const base::string16& user_input);
 
-  // Creates a NavigationRequest to use for commit. This should only be used
-  // when no appropriate NavigationRequest has been found.
-  std::unique_ptr<NavigationRequest> CreateNavigationRequestForCommit(
-      const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
-      bool is_same_document,
-      NavigationEntryImpl* entry_for_request);
-
-  // Whether the |request| corresponds to a navigation to the pending
-  // NavigationEntry. This is used at commit time, when the NavigationRequest
-  // does not match the data sent by the renderer to re-create a
-  // NavigationRequest and associate it with the pending NavigationEntry if
-  // needed.
-  // TODO(clamy): We should handle the mismatches gracefully without deleting
-  // the NavigationRequest and having to re-create one.
-  bool NavigationRequestWasIntendedForPendingEntry(
-      NavigationRequest* request,
-      const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
-      bool same_document);
+  // Returns ownership of the NavigationRequest associated with a navigation
+  // that just committed.
+  std::unique_ptr<NavigationRequest> TakeNavigationRequestForSameDocumentCommit(
+      const FrameHostMsg_DidCommitProvisionalLoad_Params& params);
+  std::unique_ptr<NavigationRequest> TakeNavigationRequestForCommit(
+      const FrameHostMsg_DidCommitProvisionalLoad_Params& params);
 
   // Helper to process the beforeunload ACK. |proceed| indicates whether the
   // navigation or tab close should be allowed to proceed.  If
@@ -1467,7 +1459,7 @@
   // The actual implementation of DidCommitProvisionalLoad and
   // DidCommitPerNavigationMojoInterfaceNavigation.
   void DidCommitNavigation(
-      std::unique_ptr<NavigationRequest> committing_navigation_request,
+      NavigationRequest* committing_navigation_request,
       std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params>
           validated_params,
       mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params);
@@ -1478,7 +1470,7 @@
   // Returns true if the navigation did commit properly, false if the commit
   // state should be restored to its pre-commit value.
   bool DidCommitNavigationInternal(
-      std::unique_ptr<NavigationRequest> navigation_request,
+      NavigationRequest* navigation_request,
       FrameHostMsg_DidCommitProvisionalLoad_Params* validated_params,
       bool is_same_document_navigation);
 
diff --git a/content/browser/indexed_db/scopes/BUILD.gn b/content/browser/indexed_db/scopes/BUILD.gn
new file mode 100644
index 0000000..209be4a
--- /dev/null
+++ b/content/browser/indexed_db/scopes/BUILD.gn
@@ -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.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("scopes_metadata_proto") {
+  visibility = [ "//content/browser/*" ]
+
+  proto_in_dir = "."
+  proto_out_dir = "content/browser/indexed_db/scopes"
+  sources = [
+    "scopes_metadata.proto",
+  ]
+
+  generate_python = false
+}
diff --git a/content/browser/indexed_db/scopes/leveldb_scopes_coding.cc b/content/browser/indexed_db/scopes/leveldb_scopes_coding.cc
new file mode 100644
index 0000000..c72fe9a
--- /dev/null
+++ b/content/browser/indexed_db/scopes/leveldb_scopes_coding.cc
@@ -0,0 +1,122 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/scopes/leveldb_scopes_coding.h"
+
+#include <utility>
+
+#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+
+namespace content {
+namespace leveldb_scopes {
+
+std::tuple<bool, int64_t> ParseScopeMetadataId(
+    leveldb::Slice key,
+    base::span<const uint8_t> scopes_prefix) {
+  int64_t scope_id = 0;
+  size_t prefix_size = scopes_prefix.size() + /*sizeof(kScopesMetadataByte)=*/1;
+
+  // The key must be larger than the prefix.
+  if (key.size() <= prefix_size)
+    return std::make_tuple(false, 0);
+
+  // The key must start with the prefix.
+  if (!key.starts_with(
+          leveldb::Slice(reinterpret_cast<const char*>(scopes_prefix.data()),
+                         scopes_prefix.size())))
+    return std::make_tuple(false, 0);
+
+  // The metadata byte must be correct.
+  if (key[scopes_prefix.size()] != kScopesMetadataByte)
+    return std::make_tuple(false, 0);
+
+  base::StringPiece part(key.data() + prefix_size, key.size() - prefix_size);
+  bool decode_success = DecodeVarInt(&part, &scope_id);
+  return std::make_tuple(decode_success, scope_id);
+}
+
+}  // namespace leveldb_scopes
+
+leveldb::Slice ScopesEncoder::GlobalMetadataKey(
+    base::span<const uint8_t> scopes_prefix) {
+  key_buffer_.clear();
+  key_buffer_.assign(reinterpret_cast<const char*>(scopes_prefix.data()),
+                     scopes_prefix.size());
+  key_buffer_.push_back(leveldb_scopes::kGlobalMetadataByte);
+  return leveldb::Slice(key_buffer_);
+}
+
+leveldb::Slice ScopesEncoder::ScopeMetadataKey(
+    base::span<const uint8_t> scopes_prefix,
+    int64_t scope_number) {
+  key_buffer_.clear();
+  key_buffer_.assign(reinterpret_cast<const char*>(scopes_prefix.data()),
+                     scopes_prefix.size());
+  key_buffer_.push_back(leveldb_scopes::kScopesMetadataByte);
+  EncodeVarInt(scope_number, &key_buffer_);
+  return leveldb::Slice(key_buffer_);
+}
+
+leveldb::Slice ScopesEncoder::ScopeMetadataPrefix(
+    base::span<const uint8_t> scopes_prefix) {
+  key_buffer_.clear();
+  key_buffer_.assign(reinterpret_cast<const char*>(scopes_prefix.data()),
+                     scopes_prefix.size());
+  key_buffer_.push_back(leveldb_scopes::kScopesMetadataByte);
+  return leveldb::Slice(key_buffer_);
+}
+
+leveldb::Slice ScopesEncoder::TasksKeyPrefix(base::span<const uint8_t> prefix,
+                                             int64_t scope_number) {
+  key_buffer_.clear();
+  key_buffer_.assign(reinterpret_cast<const char*>(prefix.data()),
+                     prefix.size());
+  key_buffer_.push_back(leveldb_scopes::kLogByte);
+  EncodeVarInt(scope_number, &key_buffer_);
+  return leveldb::Slice(key_buffer_);
+}
+
+leveldb::Slice ScopesEncoder::UndoTaskKeyPrefix(
+    base::span<const uint8_t> prefix,
+    int64_t scope_number) {
+  key_buffer_.clear();
+  key_buffer_.assign(reinterpret_cast<const char*>(prefix.data()),
+                     prefix.size());
+  key_buffer_.push_back(leveldb_scopes::kLogByte);
+  key_buffer_.push_back(leveldb_scopes::kUndoTasksByte);
+  EncodeVarInt(scope_number, &key_buffer_);
+  return leveldb::Slice(key_buffer_);
+}
+
+leveldb::Slice ScopesEncoder::CleanupTaskKeyPrefix(
+    base::span<const uint8_t> prefix,
+    int64_t scope_number) {
+  key_buffer_.clear();
+  key_buffer_.assign(reinterpret_cast<const char*>(prefix.data()),
+                     prefix.size());
+  key_buffer_.push_back(leveldb_scopes::kLogByte);
+  key_buffer_.push_back(leveldb_scopes::kCleanupTasksByte);
+  EncodeVarInt(scope_number, &key_buffer_);
+  return leveldb::Slice(key_buffer_);
+}
+
+leveldb::Slice ScopesEncoder::UndoTaskKey(
+    base::span<const uint8_t> scopes_prefix,
+    int64_t scope_number,
+    int64_t undo_sequence_number) {
+  UndoTaskKeyPrefix(scopes_prefix, scope_number);
+  EncodeInt(undo_sequence_number, &key_buffer_);
+  return leveldb::Slice(key_buffer_);
+}
+
+leveldb::Slice ScopesEncoder::CleanupTaskKey(
+    base::span<const uint8_t> scopes_prefix,
+    int64_t scope_number,
+    int64_t cleanup_sequence_number) {
+  CleanupTaskKeyPrefix(scopes_prefix, scope_number);
+  EncodeInt(cleanup_sequence_number, &key_buffer_);
+  return leveldb::Slice(key_buffer_);
+}
+
+}  // namespace content
diff --git a/content/browser/indexed_db/scopes/leveldb_scopes_coding.h b/content/browser/indexed_db/scopes/leveldb_scopes_coding.h
new file mode 100644
index 0000000..3d02efe
--- /dev/null
+++ b/content/browser/indexed_db/scopes/leveldb_scopes_coding.h
@@ -0,0 +1,104 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_SCOPES_LEVELDB_SCOPES_CODING_H_
+#define CONTENT_BROWSER_INDEXED_DB_SCOPES_LEVELDB_SCOPES_CODING_H_
+
+#include <stdint.h>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "base/containers/span.h"
+#include "content/common/content_export.h"
+#include "third_party/leveldatabase/src/include/leveldb/slice.h"
+
+namespace content {
+namespace leveldb_scopes {
+
+// TODO(dmurph): Replace all of the 'static' keywords with 'inline' when
+// chromium is updated to C++17.
+
+static constexpr uint8_t kGlobalMetadataByte = 0x00;
+static constexpr uint8_t kScopesMetadataByte = 0x01;
+static constexpr uint8_t kLogByte = 0x02;
+
+// One of these bytes follows the |kLogByte| to specify whether the log is for
+// undo tasks or a cleanup tasks.
+static constexpr uint8_t kUndoTasksByte = 0x00;
+static constexpr uint8_t kCleanupTasksByte = 0x01;
+
+static constexpr int64_t kMinSupportedVersion = 1;
+static constexpr int64_t kCurrentVersion = 1;
+
+static constexpr int64_t kFirstScopeNumber = 0;
+
+CONTENT_EXPORT std::tuple<bool /*success*/, int64_t /*scope_id*/>
+ParseScopeMetadataId(leveldb::Slice key,
+                     base::span<const uint8_t> scopes_prefix);
+
+}  // namespace leveldb_scopes
+
+// This class helps the re-use of a common std::string buffer. All calls modify
+// the internal std::string buffer and return a slice to it.
+// Important: Every call to this class will invalidate any 'old' slices that
+// were returned by previous calls.
+class CONTENT_EXPORT ScopesEncoder {
+ public:
+  ScopesEncoder() = default;
+  ~ScopesEncoder() = default;
+
+  // The value on disk is expected to be a LevelDBScopesMetadata.
+  leveldb::Slice GlobalMetadataKey(base::span<const uint8_t> scopes_prefix);
+
+  // The value on disk  is expected to be a LevelDBScopesScopeMetadata.
+  leveldb::Slice ScopeMetadataKey(base::span<const uint8_t> scopes_prefix,
+                                  int64_t scope_number);
+
+  // Returns a key prefix that only scope metadata keys use. This is intended to
+  // be used to initialize a LevelDB iterator, where advancing the iterator
+  // enumerates all metadata entries. Each metadata entry value is expected to
+  // be a LevelDBScopesScopeMetadata.
+  leveldb::Slice ScopeMetadataPrefix(base::span<const uint8_t> scopes_prefix);
+
+  // Returns a key prefix that only scope tasks keys for the given
+  // |scope_number| use. This is intended to be used to delete all tasks, or
+  // create an iterator for all tasks. There are two task types under this
+  // prefix, undo tasks and cleanup tasks.
+  leveldb::Slice TasksKeyPrefix(base::span<const uint8_t> scopes_prefix,
+                                int64_t scope_number);
+
+  // Returns a key prefix is only scoped to 'undo' tasks keys for the given
+  // |scope_number| use. This is intended to be used to initialize a LevelDB
+  // iterator, where advancing the iterator enumerates all of the tasks for the
+  // given |scope_number|. The task value is expected to be a
+  // LevelDBScopesUndoTask.
+  leveldb::Slice UndoTaskKeyPrefix(base::span<const uint8_t> scopes_prefix,
+                                   int64_t scope_number);
+
+  // Returns a key prefix is only scoped to 'cleanup' tasks keys for the given
+  // |scope_number| use. This is intended to be used to initialize a LevelDB
+  // iterator, where advancing the iterator enumerates all of the tasks for the
+  // given |scope_number|.  The task value is expected to be a
+  // LevelDBScopesCleanupTask.
+  leveldb::Slice CleanupTaskKeyPrefix(base::span<const uint8_t> scopes_prefix,
+                                      int64_t scope_number);
+
+  // The value on disk is expected to be a LevelDBScopesUndoTask.
+  leveldb::Slice UndoTaskKey(base::span<const uint8_t> scopes_prefix,
+                             int64_t scope_number,
+                             int64_t undo_sequence_number);
+
+  // The value on disk is expected to be a LevelDBScopesCleanupTask.
+  leveldb::Slice CleanupTaskKey(base::span<const uint8_t> scopes_prefix,
+                                int64_t scope_number,
+                                int64_t cleanup_sequence_number);
+
+ private:
+  std::string key_buffer_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_INDEXED_DB_SCOPES_LEVELDB_SCOPES_CODING_H_
diff --git a/content/browser/indexed_db/scopes/leveldb_scopes_coding_unittest.cc b/content/browser/indexed_db/scopes/leveldb_scopes_coding_unittest.cc
new file mode 100644
index 0000000..b0a668a
--- /dev/null
+++ b/content/browser/indexed_db/scopes/leveldb_scopes_coding_unittest.cc
@@ -0,0 +1,168 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/scopes/leveldb_scopes_coding.h"
+
+#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+namespace {
+
+TEST(LevelDBScopesCodingTest, GlobalMetadataKey) {
+  std::vector<uint8_t> scopes_prefix = {'A', 'B'};
+  ScopesEncoder encoder;
+
+  std::string expected = "AB";
+  expected.push_back(0x00);
+  EXPECT_EQ(leveldb::Slice(expected), encoder.GlobalMetadataKey(scopes_prefix));
+}
+
+TEST(LevelDBScopesCodingTest, ScopeMetadataKey) {
+  const static int kScopeNumber = 513;
+  std::vector<uint8_t> scopes_prefix = {'A', 'B'};
+  ScopesEncoder encoder;
+
+  std::string expected = "AB\x01";
+  EncodeVarInt(kScopeNumber, &expected);
+  EXPECT_EQ(leveldb::Slice(expected),
+            encoder.ScopeMetadataKey(scopes_prefix, kScopeNumber));
+}
+
+TEST(LevelDBScopesCodingTest, ScopeMetadataPrefix) {
+  std::vector<uint8_t> scopes_prefix = {'A', 'B'};
+  ScopesEncoder encoder;
+
+  std::string expected = "AB\x01";
+  EXPECT_EQ(leveldb::Slice(expected),
+            encoder.ScopeMetadataPrefix(scopes_prefix));
+}
+
+TEST(LevelDBScopesCodingTest, TasksKeyPrefix) {
+  const static int kScopeNumber = 1025;
+  std::vector<uint8_t> scopes_prefix = {'A', 'B'};
+  ScopesEncoder encoder;
+
+  std::string expected = "AB\x02";
+  EncodeVarInt(kScopeNumber, &expected);
+  EXPECT_EQ(leveldb::Slice(expected),
+            encoder.TasksKeyPrefix(scopes_prefix, kScopeNumber));
+}
+
+TEST(LevelDBScopesCodingTest, UndoTaskKeyPrefix) {
+  const static int kScopeNumber = 1025;
+  std::vector<uint8_t> scopes_prefix = {'A', 'B'};
+  ScopesEncoder encoder;
+
+  std::string expected = "AB\x02";
+  expected.push_back(0x00);
+  EncodeVarInt(kScopeNumber, &expected);
+  EXPECT_EQ(leveldb::Slice(expected),
+            encoder.UndoTaskKeyPrefix(scopes_prefix, kScopeNumber));
+}
+
+TEST(LevelDBScopesCodingTest, CleanupTaskKeyPrefix) {
+  const static int kScopeNumber = 1025;
+  std::vector<uint8_t> scopes_prefix = {'A', 'B'};
+  ScopesEncoder encoder;
+
+  std::string expected = "AB\x02\x01";
+  EncodeVarInt(kScopeNumber, &expected);
+  EXPECT_EQ(leveldb::Slice(expected),
+            encoder.CleanupTaskKeyPrefix(scopes_prefix, kScopeNumber));
+}
+
+TEST(LevelDBScopesCodingTest, UndoTaskKey) {
+  const static int kScopeNumber = 1025;
+  const static int kSequenceNumber = 1025;
+  std::vector<uint8_t> scopes_prefix = {'A', 'B'};
+  ScopesEncoder encoder;
+
+  std::string expected = "AB\x02";
+  expected.push_back(0x00);
+  EncodeVarInt(kScopeNumber, &expected);
+  EncodeInt(kScopeNumber, &expected);
+  EXPECT_EQ(leveldb::Slice(expected),
+            encoder.UndoTaskKey(scopes_prefix, kScopeNumber, kSequenceNumber));
+}
+
+TEST(LevelDBScopesCodingTest, CleanupTaskKey) {
+  const static int kScopeNumber = 1025;
+  const static int kSequenceNumber = 1025;
+  std::vector<uint8_t> scopes_prefix = {'A', 'B'};
+  ScopesEncoder encoder;
+
+  std::string expected = "AB\x02\x01";
+  EncodeVarInt(kScopeNumber, &expected);
+  EncodeInt(kScopeNumber, &expected);
+  EXPECT_EQ(
+      leveldb::Slice(expected),
+      encoder.CleanupTaskKey(scopes_prefix, kScopeNumber, kSequenceNumber));
+}
+
+TEST(LevelDBScopesCodingTest, ParseScopeMetadataId) {
+  const static int kScopeNumber = 1025;
+  std::vector<uint8_t> scopes_prefix = {'A', 'B'};
+  bool success = false;
+  int64_t scope_id;
+
+  std::string on_disk = "AB\x01";
+  EncodeVarInt(kScopeNumber, &on_disk);
+  std::tie(success, scope_id) =
+      leveldb_scopes::ParseScopeMetadataId(on_disk, scopes_prefix);
+  EXPECT_TRUE(success);
+  EXPECT_EQ(kScopeNumber, scope_id);
+}
+
+TEST(LevelDBScopesCodingTest, InvalidMetadataByte) {
+  const static int kScopeNumber = 1025;
+  std::vector<uint8_t> scopes_prefix = {'A', 'B'};
+  bool success = false;
+  int64_t scope_id;
+
+  // Wrong metadata byte.
+  std::string on_disk = "AB\x02";
+  EncodeVarInt(kScopeNumber, &on_disk);
+  EncodeInt(0, &on_disk);
+  std::tie(success, scope_id) =
+      leveldb_scopes::ParseScopeMetadataId(on_disk, scopes_prefix);
+  EXPECT_FALSE(success);
+}
+
+TEST(LevelDBScopesCodingTest, InvalidVarInt) {
+  std::vector<uint8_t> scopes_prefix = {'A', 'B'};
+  bool success = false;
+  int64_t scope_id;
+
+  // Invalid varint
+  std::string on_disk = "AB\x01\xFF";
+  std::tie(success, scope_id) =
+      leveldb_scopes::ParseScopeMetadataId(on_disk, scopes_prefix);
+  EXPECT_FALSE(success);
+  on_disk = "AB\x01";
+  std::tie(success, scope_id) =
+      leveldb_scopes::ParseScopeMetadataId(on_disk, scopes_prefix);
+  EXPECT_FALSE(success);
+}
+
+TEST(LevelDBScopesCodingTest, InvalidPrefix) {
+  const static int kScopeNumber = 1025;
+  std::vector<uint8_t> scopes_prefix = {'A', 'B'};
+
+  bool success = false;
+  int64_t scope_id;
+  // Invalid prefix
+  std::string on_disk = "XX\x01";
+  EncodeVarInt(kScopeNumber, &on_disk);
+  std::tie(success, scope_id) =
+      leveldb_scopes::ParseScopeMetadataId(on_disk, scopes_prefix);
+  EXPECT_FALSE(success);
+  on_disk = "A";
+  std::tie(success, scope_id) =
+      leveldb_scopes::ParseScopeMetadataId(on_disk, scopes_prefix);
+  EXPECT_FALSE(success);
+}
+
+}  // namespace
+}  // namespace content
diff --git a/content/browser/indexed_db/scopes/scopes_metadata.proto b/content/browser/indexed_db/scopes/scopes_metadata.proto
new file mode 100644
index 0000000..0327221
--- /dev/null
+++ b/content/browser/indexed_db/scopes/scopes_metadata.proto
@@ -0,0 +1,48 @@
+// 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.
+syntax = "proto3";
+
+option optimize_for = LITE_RUNTIME;
+
+package content;
+
+message LevelDBScopesKeyRange {
+  bytes begin = 1;
+  bytes end = 2;
+}
+
+message LevelDBScopesMetadata {
+  int64 version = 1;
+}
+
+message LevelDBScopesScopeMetadata {
+  message LevelDBScopesLock {
+    int64 level = 1;
+    LevelDBScopesKeyRange range = 2;
+  }
+  repeated LevelDBScopesLock locks = 1;
+
+  // If this is true, don't execute cleanup tasks when cleaning up this scope.
+  bool ignore_cleanup_tasks = 2;
+}
+
+message LevelDBScopesUndoTask {
+  message Put {
+    bytes key = 1;
+    bytes value = 2;
+  }
+  message Delete { bytes key = 1; }
+  oneof operation {
+    Put put = 1;
+    Delete delete = 2;
+    LevelDBScopesKeyRange delete_range = 3;
+  }
+}
+
+message LevelDBScopesCleanupTask {
+  oneof operation {
+    LevelDBScopesKeyRange delete_range = 1;
+    LevelDBScopesKeyRange delete_range_and_compact = 2;
+  }
+}
\ No newline at end of file
diff --git a/content/browser/media/media_keys_listener_manager_impl.cc b/content/browser/media/media_keys_listener_manager_impl.cc
index 386c890..6e0472dc 100644
--- a/content/browser/media/media_keys_listener_manager_impl.cc
+++ b/content/browser/media/media_keys_listener_manager_impl.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "build/build_config.h"
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/media/hardware_key_media_controller.h"
 #include "ui/base/accelerators/accelerator.h"
@@ -16,6 +17,11 @@
 #include "ui/base/mpris/mpris_service.h"  // nogncheck
 #endif
 
+#if defined(OS_MACOSX)
+#include "content/browser/media/now_playing_info_center_notifier.h"
+#include "ui/base/now_playing/now_playing_info_center_delegate.h"
+#endif
+
 namespace content {
 
 MediaKeysListenerManagerImpl::ListeningData::ListeningData()
@@ -33,8 +39,9 @@
 
 MediaKeysListenerManagerImpl::MediaKeysListenerManagerImpl(
     service_manager::Connector* connector)
-    : hardware_key_media_controller_(
-          std::make_unique<HardwareKeyMediaController>(connector)),
+    : connector_(connector),
+      hardware_key_media_controller_(
+          std::make_unique<HardwareKeyMediaController>(connector_)),
       media_key_handling_enabled_(true),
       auxiliary_services_started_(false) {
   DCHECK(!MediaKeysListenerManager::GetInstance());
@@ -140,6 +147,18 @@
   mpris::MprisService::GetInstance()->StartService();
 #endif
 
+#if defined(OS_MACOSX)
+  // Only create the NowPlayingInfoCenterNotifier if we're able to get a
+  // NowPlayingInfoCenterDelegate.
+  auto now_playing_info_center_delegate =
+      now_playing::NowPlayingInfoCenterDelegate::Create();
+  if (now_playing_info_center_delegate) {
+    now_playing_info_center_notifier_ =
+        std::make_unique<NowPlayingInfoCenterNotifier>(
+            connector_, std::move(now_playing_info_center_delegate));
+  }
+#endif
+
   auxiliary_services_started_ = true;
 }
 
diff --git a/content/browser/media/media_keys_listener_manager_impl.h b/content/browser/media/media_keys_listener_manager_impl.h
index 6a67f75..be19904 100644
--- a/content/browser/media/media_keys_listener_manager_impl.h
+++ b/content/browser/media/media_keys_listener_manager_impl.h
@@ -10,6 +10,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/observer_list.h"
+#include "build/build_config.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/media_keys_listener_manager.h"
 #include "ui/base/accelerators/media_keys_listener.h"
@@ -23,6 +24,10 @@
 
 class HardwareKeyMediaController;
 
+#if defined(OS_MACOSX)
+class NowPlayingInfoCenterNotifier;
+#endif
+
 // Listens for media keys and decides which listeners receive which events. In
 // particular, it owns one of its delegates (HardwareKeyMediaController), and
 // only propagates to the HardwareKeyMediaController if no other delegates are
@@ -94,6 +99,7 @@
   base::flat_map<ui::KeyboardCode, std::unique_ptr<ListeningData>>
       delegate_map_;
   std::unique_ptr<ui::MediaKeysListener> media_keys_listener_;
+  service_manager::Connector* connector_;
   std::unique_ptr<HardwareKeyMediaController> hardware_key_media_controller_;
 
   // False if media key handling has been explicitly disabled by a call to
@@ -103,6 +109,11 @@
   // True if auxiliary services have already been started.
   bool auxiliary_services_started_;
 
+#if defined(OS_MACOSX)
+  std::unique_ptr<NowPlayingInfoCenterNotifier>
+      now_playing_info_center_notifier_;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(MediaKeysListenerManagerImpl);
 };
 
diff --git a/content/browser/media/now_playing_info_center_notifier.cc b/content/browser/media/now_playing_info_center_notifier.cc
new file mode 100644
index 0000000..9cb626d
--- /dev/null
+++ b/content/browser/media/now_playing_info_center_notifier.cc
@@ -0,0 +1,63 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/media/now_playing_info_center_notifier.h"
+
+#include <memory>
+#include <utility>
+
+#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/now_playing/now_playing_info_center_delegate.h"
+
+namespace content {
+
+NowPlayingInfoCenterNotifier::NowPlayingInfoCenterNotifier(
+    service_manager::Connector* connector,
+    std::unique_ptr<now_playing::NowPlayingInfoCenterDelegate>
+        now_playing_info_center_delegate)
+    : now_playing_info_center_delegate_(
+          std::move(now_playing_info_center_delegate)) {
+  // |connector| can be null in tests.
+  if (!connector)
+    return;
+
+  // Connect to the MediaControllerManager and create a MediaController that
+  // controls the active session so we can observe it.
+  media_session::mojom::MediaControllerManagerPtr controller_manager_ptr;
+  connector->BindInterface(media_session::mojom::kServiceName,
+                           mojo::MakeRequest(&controller_manager_ptr));
+  controller_manager_ptr->CreateActiveMediaController(
+      mojo::MakeRequest(&media_controller_ptr_));
+
+  // Observe the active media controller for changes to playback state and
+  // supported actions.
+  media_session::mojom::MediaControllerObserverPtr media_controller_observer;
+  media_controller_observer_binding_.Bind(
+      mojo::MakeRequest(&media_controller_observer));
+  media_controller_ptr_->AddObserver(std::move(media_controller_observer));
+}
+
+NowPlayingInfoCenterNotifier::~NowPlayingInfoCenterNotifier() = default;
+
+void NowPlayingInfoCenterNotifier::MediaSessionInfoChanged(
+    media_session::mojom::MediaSessionInfoPtr session_info) {
+  session_info_ = std::move(session_info);
+  if (session_info_) {
+    if (session_info_->playback_state ==
+        media_session::mojom::MediaPlaybackState::kPlaying) {
+      now_playing_info_center_delegate_->SetPlaybackState(
+          now_playing::NowPlayingInfoCenterDelegate::PlaybackState::kPlaying);
+    } else {
+      now_playing_info_center_delegate_->SetPlaybackState(
+          now_playing::NowPlayingInfoCenterDelegate::PlaybackState::kPaused);
+    }
+  } else {
+    now_playing_info_center_delegate_->SetPlaybackState(
+        now_playing::NowPlayingInfoCenterDelegate::PlaybackState::kStopped);
+  }
+}
+
+}  // namespace content
diff --git a/content/browser/media/now_playing_info_center_notifier.h b/content/browser/media/now_playing_info_center_notifier.h
new file mode 100644
index 0000000..8f66d886
--- /dev/null
+++ b/content/browser/media/now_playing_info_center_notifier.h
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_MEDIA_NOW_PLAYING_INFO_CENTER_NOTIFIER_H_
+#define CONTENT_BROWSER_MEDIA_NOW_PLAYING_INFO_CENTER_NOTIFIER_H_
+
+#include <memory>
+#include <vector>
+
+#include "content/common/content_export.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/media_session/public/mojom/media_controller.mojom.h"
+
+namespace now_playing {
+class NowPlayingInfoCenterDelegate;
+}  // namespace now_playing
+
+namespace service_manager {
+class Connector;
+}  // namespace service_manager
+
+namespace content {
+
+// The NowPlayingInfoCenterNotifier connects to Mac OS's "Now Playing" info
+// center and keeps the OS informed of the current media playstate and metadata.
+// It observes changes to the active Media Session and updates the info center
+// accordingly.
+class CONTENT_EXPORT NowPlayingInfoCenterNotifier
+    : public media_session::mojom::MediaControllerObserver {
+ public:
+  NowPlayingInfoCenterNotifier(
+      service_manager::Connector* connector,
+      std::unique_ptr<now_playing::NowPlayingInfoCenterDelegate>
+          now_playing_info_center_delegate);
+  ~NowPlayingInfoCenterNotifier() override;
+
+  // media_session::mojom::MediaControllerObserver implementation.
+  void MediaSessionInfoChanged(
+      media_session::mojom::MediaSessionInfoPtr session_info) override;
+  void MediaSessionMetadataChanged(
+      const base::Optional<media_session::MediaMetadata>& metadata) override {}
+  void MediaSessionActionsChanged(
+      const std::vector<media_session::mojom::MediaSessionAction>& actions)
+      override {}
+  void MediaSessionChanged(
+      const base::Optional<base::UnguessableToken>& request_id) override {}
+
+ private:
+  // Our connection to the underlying OS API for MPNowPlayingInfoCenter.
+  std::unique_ptr<now_playing::NowPlayingInfoCenterDelegate>
+      now_playing_info_center_delegate_;
+
+  // Tracks current media session state/metadata.
+  media_session::mojom::MediaControllerPtr media_controller_ptr_;
+  media_session::mojom::MediaSessionInfoPtr session_info_;
+
+  // Used to receive updates to the active media controller.
+  mojo::Binding<media_session::mojom::MediaControllerObserver>
+      media_controller_observer_binding_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(NowPlayingInfoCenterNotifier);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_MEDIA_NOW_PLAYING_INFO_CENTER_NOTIFIER_H_
diff --git a/content/browser/media/now_playing_info_center_notifier_unittest.cc b/content/browser/media/now_playing_info_center_notifier_unittest.cc
new file mode 100644
index 0000000..4b588966
--- /dev/null
+++ b/content/browser/media/now_playing_info_center_notifier_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/media/now_playing_info_center_notifier.h"
+
+#include <memory>
+#include <utility>
+
+#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/now_playing/mock_now_playing_info_center_delegate.h"
+
+namespace content {
+
+using media_session::mojom::MediaPlaybackState;
+using media_session::mojom::MediaSessionInfo;
+using media_session::mojom::MediaSessionInfoPtr;
+
+class NowPlayingInfoCenterNotifierTest : public testing::Test {
+ public:
+  NowPlayingInfoCenterNotifierTest() = default;
+  ~NowPlayingInfoCenterNotifierTest() override = default;
+
+  void SetUp() override {
+    auto mock_now_playing_info_center_delegate =
+        std::make_unique<now_playing::MockNowPlayingInfoCenterDelegate>();
+    mock_now_playing_info_center_delegate_ =
+        mock_now_playing_info_center_delegate.get();
+    notifier_ = std::make_unique<NowPlayingInfoCenterNotifier>(
+        /*connector=*/nullptr,
+        std::move(mock_now_playing_info_center_delegate));
+  }
+
+ protected:
+  void SimulatePlaying() {
+    MediaSessionInfoPtr session_info(MediaSessionInfo::New());
+    session_info->playback_state = MediaPlaybackState::kPlaying;
+    notifier_->MediaSessionInfoChanged(std::move(session_info));
+  }
+
+  void SimulatePaused() {
+    MediaSessionInfoPtr session_info(MediaSessionInfo::New());
+    session_info->playback_state = MediaPlaybackState::kPaused;
+    notifier_->MediaSessionInfoChanged(std::move(session_info));
+  }
+
+  void SimulateStopped() { notifier_->MediaSessionInfoChanged(nullptr); }
+
+  NowPlayingInfoCenterNotifier& notifier() { return *notifier_; }
+  now_playing::MockNowPlayingInfoCenterDelegate&
+  mock_now_playing_info_center_delegate() {
+    return *mock_now_playing_info_center_delegate_;
+  }
+
+ private:
+  std::unique_ptr<NowPlayingInfoCenterNotifier> notifier_;
+  now_playing::MockNowPlayingInfoCenterDelegate*
+      mock_now_playing_info_center_delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(NowPlayingInfoCenterNotifierTest);
+};
+
+TEST_F(NowPlayingInfoCenterNotifierTest, ProperlyUpdatesPlaybackState) {
+  EXPECT_CALL(
+      mock_now_playing_info_center_delegate(),
+      SetPlaybackState(
+          now_playing::NowPlayingInfoCenterDelegate::PlaybackState::kPlaying))
+      .Times(3);
+  EXPECT_CALL(
+      mock_now_playing_info_center_delegate(),
+      SetPlaybackState(
+          now_playing::NowPlayingInfoCenterDelegate::PlaybackState::kPaused))
+      .Times(2);
+  EXPECT_CALL(
+      mock_now_playing_info_center_delegate(),
+      SetPlaybackState(
+          now_playing::NowPlayingInfoCenterDelegate::PlaybackState::kStopped))
+      .Times(1);
+
+  SimulatePlaying();
+  SimulatePaused();
+  SimulatePlaying();
+  SimulateStopped();
+  SimulatePlaying();
+  SimulatePaused();
+}
+
+}  // namespace content
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index e536d1e..7471887 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -1166,6 +1166,97 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
 }
 
+// This test reproduces the following race condition:
+// 1) A first navigation starts, the headers are received, the navigation
+//    reaches ready-to-commit. It is sent to the renderer to be committed.
+// 2) In the meantime, a second navigation reaches ready-to-commit in the
+//    browser.
+// 3) Before the renderer gets notified of the new navigation, the
+//    first navigation is committed.
+// 4) The browser gets notified of the commit of the first navigation. This
+//    should not destroy the NavigationRequest corresponding to the second
+//    navigation.
+IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
+                       RaceNewNavigationCommitWhileOldOneFinishesLoading) {
+  // Start the test with an initial document.
+  GURL main_url(embedded_test_server()->GetURL("/simple_page.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  RenderFrameHostImpl* render_frame_host = static_cast<RenderFrameHostImpl*>(
+      shell()->web_contents()->GetMainFrame());
+
+  NavigationRecorder recorder(shell()->web_contents());
+  // Note: These two pages contain an image that will never load. The goal is to
+  // prevent RenderFrameHostImpl::DidStopLoading() to be called since it will
+  // cancel any pending navigation.
+  GURL page_1(embedded_test_server()->GetURL("/infinite_load_1.html"));
+  GURL page_2(embedded_test_server()->GetURL("/infinite_load_2.html"));
+  // Intercept and cancel any FrameMsgHost_DidCommitProvisionalLoad events.
+  InterceptAndCancelDidCommitProvisionalLoad interceptor(
+      shell()->web_contents());
+
+  // 1) Navigate to page_1.
+  shell()->LoadURL(page_1);
+
+  // 2) The browser receives the response's headers. The navigation commits in
+  //    the browser.
+  recorder.WaitForEvents(2);
+  EXPECT_EQ(2u, recorder.records().size());
+  EXPECT_STREQ("start /infinite_load_1.html", recorder.records()[0].c_str());
+  EXPECT_STREQ("ready-to-commit /infinite_load_1.html",
+               recorder.records()[1].c_str());
+
+  // 3) Wait for the renderer to receive the response's body, but do not notify
+  //    the browser of it right now. It is delayed in 6).
+  interceptor.Wait(1);
+  EXPECT_EQ(1u, interceptor.intercepted_messages().size());
+
+  // 4) In the meantime, the browser starts a navigation to page_2.
+  shell()->LoadURL(page_2);
+
+  // 5) The response's headers are received, the navigation reaches
+  // ready-to-commit in the browser. This should not delete the ongoing
+  // NavigationRequest.
+  recorder.WaitForEvents(4);
+  EXPECT_EQ(4u, recorder.records().size());
+  EXPECT_STREQ("start /infinite_load_2.html", recorder.records()[2].c_str());
+  EXPECT_STREQ("ready-to-commit /infinite_load_2.html",
+               recorder.records()[3].c_str());
+
+  // 6) The browser receives the first DidCommitProvisionalLoad message. This
+  //    should not delete the second navigation. This is the end of the first
+  //    navigation.
+  ASSERT_GE(interceptor.intercepted_navigations().size(), 1u);
+  render_frame_host->DidCommitNavigationForTesting(
+      interceptor.intercepted_navigations()[0],
+      std::make_unique<::FrameHostMsg_DidCommitProvisionalLoad_Params>(
+          interceptor.intercepted_messages()[0]),
+      mojom::DidCommitProvisionalLoadInterfaceParams::New(
+          std::move(interceptor.intercepted_requests()[0]),
+          std::move(interceptor.intercepted_broker_content_requests()[0]),
+          std::move(interceptor.intercepted_broker_blink_requests()[0])));
+  recorder.WaitForEvents(5);
+  EXPECT_EQ(5u, recorder.records().size());
+  EXPECT_STREQ("did-commit /infinite_load_1.html",
+               recorder.records()[4].c_str());
+
+  // 7) Wait for the renderer to receive the second response's body. This is the
+  //    end of the second navigation.
+  interceptor.Wait(2);
+  EXPECT_EQ(2u, interceptor.intercepted_messages().size());
+  render_frame_host->DidCommitNavigationForTesting(
+      interceptor.intercepted_navigations()[1],
+      std::make_unique<::FrameHostMsg_DidCommitProvisionalLoad_Params>(
+          interceptor.intercepted_messages()[1]),
+      mojom::DidCommitProvisionalLoadInterfaceParams::New(
+          std::move(interceptor.intercepted_requests()[1]),
+          std::move(interceptor.intercepted_broker_content_requests()[1]),
+          std::move(interceptor.intercepted_broker_blink_requests()[1])));
+  recorder.WaitForEvents(6);
+  EXPECT_EQ(6u, recorder.records().size());
+  EXPECT_STREQ("did-commit /infinite_load_2.html",
+               recorder.records()[5].c_str());
+}
+
 // Renderer initiated back/forward navigation in beforeunload should not prevent
 // the user to navigate away from a website.
 IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, HistoryBackInBeforeUnload) {
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index a6f4cb5b..91a11cc 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -27,6 +27,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/process_type.h"
 #include "content/public/common/sandboxed_process_launcher_delegate.h"
@@ -325,6 +326,10 @@
     // not needed on Android anyway. See crbug.com/500854.
     std::unique_ptr<base::CommandLine> cmd_line =
         std::make_unique<base::CommandLine>(base::CommandLine::NO_PROGRAM);
+    if (sandbox_type_ == service_manager::SANDBOX_TYPE_NETWORK &&
+        base::FeatureList::IsEnabled(features::kWarmUpNetworkProcess)) {
+      process_->EnableWarmUpConnection();
+    }
 #else
     int child_flags = child_flags_;
 
diff --git a/content/browser/webauth/authenticator_impl.cc b/content/browser/webauth/authenticator_impl.cc
index 0ae9433..3cf5f7f 100644
--- a/content/browser/webauth/authenticator_impl.cc
+++ b/content/browser/webauth/authenticator_impl.cc
@@ -963,8 +963,6 @@
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kKeyAlreadyRegistered);
       return;
-    case device::FidoReturnCode::kAuthenticatorRemovedDuringPINEntry:
-      [[fallthrough]];
     case device::FidoReturnCode::kAuthenticatorResponseInvalid:
       // The response from the authenticator was corrupted.
       InvokeCallbackAndCleanup(
@@ -992,6 +990,11 @@
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kHardPINBlock);
       return;
+    case device::FidoReturnCode::kAuthenticatorRemovedDuringPINEntry:
+      SignalFailureToRequestDelegate(
+          AuthenticatorRequestClientDelegate::InterestingFailureReason::
+              kAuthenticatorRemovedDuringPINEntry);
+      return;
     case device::FidoReturnCode::kSuccess:
       DCHECK(response_data.has_value());
 
@@ -1112,8 +1115,11 @@
 
 void AuthenticatorImpl::OnSignResponse(
     device::FidoReturnCode status_code,
-    base::Optional<device::AuthenticatorGetAssertionResponse> response_data,
+    base::Optional<std::vector<device::AuthenticatorGetAssertionResponse>>
+        response_data,
     base::Optional<device::FidoTransportProtocol> transport_used) {
+  DCHECK(!response_data || !response_data->empty());  // empty vector is invalid
+
   if (!request_) {
     // Either the callback was called immediately and |request_| has not yet
     // been assigned (this is a bug), or a navigation caused the request to be
@@ -1127,8 +1133,6 @@
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kKeyNotRegistered);
       return;
-    case device::FidoReturnCode::kAuthenticatorRemovedDuringPINEntry:
-      [[fallthrough]];
     case device::FidoReturnCode::kAuthenticatorResponseInvalid:
       // The response from the authenticator was corrupted.
       InvokeCallbackAndCleanup(
@@ -1154,23 +1158,31 @@
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kHardPINBlock);
       return;
+    case device::FidoReturnCode::kAuthenticatorRemovedDuringPINEntry:
+      SignalFailureToRequestDelegate(
+          AuthenticatorRequestClientDelegate::InterestingFailureReason::
+              kAuthenticatorRemovedDuringPINEntry);
+      return;
     case device::FidoReturnCode::kSuccess:
       DCHECK(response_data.has_value());
+      // Resident keys are not yet supported so there must be a single response.
+      DCHECK_EQ(1u, response_data->size());
+      auto& response = response_data.value()[0];
+
       if (transport_used) {
         request_delegate_->UpdateLastTransportUsed(*transport_used);
       }
 
       base::Optional<bool> echo_appid_extension;
       if (app_id_) {
-        echo_appid_extension = (response_data->GetRpIdHash() ==
-                                CreateApplicationParameter(*app_id_));
+        echo_appid_extension =
+            (response.GetRpIdHash() == CreateApplicationParameter(*app_id_));
       }
-      InvokeCallbackAndCleanup(
-          std::move(get_assertion_response_callback_),
-          blink::mojom::AuthenticatorStatus::SUCCESS,
-          CreateGetAssertionResponse(std::move(client_data_json_),
-                                     std::move(*response_data),
-                                     echo_appid_extension));
+      InvokeCallbackAndCleanup(std::move(get_assertion_response_callback_),
+                               blink::mojom::AuthenticatorStatus::SUCCESS,
+                               CreateGetAssertionResponse(
+                                   std::move(client_data_json_),
+                                   std::move(response), echo_appid_extension));
       return;
   }
   NOTREACHED();
@@ -1200,6 +1212,11 @@
     case AuthenticatorRequestClientDelegate::InterestingFailureReason::
         kHardPINBlock:
       status = blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR;
+      break;
+    case AuthenticatorRequestClientDelegate::InterestingFailureReason::
+        kAuthenticatorRemovedDuringPINEntry:
+      status = blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR;
+      break;
   }
 
   error_awaiting_user_acknowledgement_ = status;
diff --git a/content/browser/webauth/authenticator_impl.h b/content/browser/webauth/authenticator_impl.h
index 538ea05..8a5d32c 100644
--- a/content/browser/webauth/authenticator_impl.h
+++ b/content/browser/webauth/authenticator_impl.h
@@ -9,6 +9,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/containers/flat_set.h"
 #include "base/containers/span.h"
@@ -150,7 +151,8 @@
   // Callback to handle the async response from a U2fDevice.
   void OnSignResponse(
       device::FidoReturnCode status_code,
-      base::Optional<device::AuthenticatorGetAssertionResponse> response_data,
+      base::Optional<std::vector<device::AuthenticatorGetAssertionResponse>>
+          response_data,
       base::Optional<device::FidoTransportProtocol> transport_used);
 
   void FailWithErrorAndCleanup();
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index c04a2e9..04593738 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -2611,6 +2611,26 @@
   EXPECT_TRUE(HasUV(callback_receiver));
 }
 
+TEST_F(PINAuthenticatorImplTest, MakeCredentialNoPIN) {
+  TestServiceManagerContext smc;
+
+  for (const auto uv : {blink::mojom::UserVerificationRequirement::DISCOURAGED,
+                        blink::mojom::UserVerificationRequirement::PREFERRED}) {
+    // If no PIN is configured, then even "preferring" does not trigger setting
+    // a PIN.
+    test_client_.expected.clear();
+
+    AuthenticatorPtr authenticator = ConnectToAuthenticator();
+    TestMakeCredentialCallback callback_receiver;
+    authenticator->MakeCredential(make_credential_options(uv),
+                                  callback_receiver.callback());
+    callback_receiver.WaitForCallback();
+    EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status());
+    EXPECT_EQ("", virtual_device_.mutable_state()->pin);
+    EXPECT_FALSE(HasUV(callback_receiver));
+  }
+}
+
 TEST_F(PINAuthenticatorImplTest, MakeCredentialUse) {
   TestServiceManagerContext smc;
   virtual_device_.mutable_state()->pin = kTestPIN;
@@ -2683,6 +2703,29 @@
   EXPECT_TRUE(HasUV(callback_receiver));
 }
 
+TEST_F(PINAuthenticatorImplTest, GetAssertionNoPIN) {
+  TestServiceManagerContext smc;
+  ASSERT_TRUE(virtual_device_.mutable_state()->InjectRegistration(
+      get_credential_options()->allow_credentials[0]->id, kTestRelyingPartyId));
+
+  for (const auto uv : {blink::mojom::UserVerificationRequirement::DISCOURAGED,
+                        blink::mojom::UserVerificationRequirement::PREFERRED}) {
+    SCOPED_TRACE(UVToString(uv));
+
+    // Requests should succeed but not return UV and no PIN prompts are
+    // expected.
+    test_client_.expected.clear();
+    AuthenticatorPtr authenticator = ConnectToAuthenticator();
+    TestGetAssertionCallback callback_receiver;
+    authenticator->GetAssertion(get_credential_options(uv),
+                                callback_receiver.callback());
+    callback_receiver.WaitForCallback();
+
+    EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status());
+    EXPECT_FALSE(HasUV(callback_receiver));
+  }
+}
+
 TEST_F(PINAuthenticatorImplTest, GetAssertionSoftLock) {
   TestServiceManagerContext smc;
   virtual_device_.mutable_state()->pin = kTestPIN;
@@ -2819,16 +2862,10 @@
                   callback_receiver.status());
       } else {
         EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status());
-
-        if (false) {
-          // TODO: make this true as I believe it's the correct behaviour.
-          EXPECT_EQ(
-              fingerprints_enrolled &&
-                  uv != blink::mojom::UserVerificationRequirement::DISCOURAGED,
-              HasUV(callback_receiver));
-        } else {
-          EXPECT_EQ(fingerprints_enrolled, HasUV(callback_receiver));
-        }
+        EXPECT_EQ(
+            fingerprints_enrolled &&
+                uv != blink::mojom::UserVerificationRequirement::DISCOURAGED,
+            HasUV(callback_receiver));
       }
     }
   }
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 6085baba..2cf2f2db 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -613,14 +613,10 @@
   return &native_theme_engine_;
 }
 
-blink::Platform::FileHandle BlinkPlatformImpl::DatabaseOpenFile(
+base::File BlinkPlatformImpl::DatabaseOpenFile(
     const blink::WebString& vfs_file_name,
     int desired_flags) {
-#if defined(OS_WIN)
-  return INVALID_HANDLE_VALUE;
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-  return -1;
-#endif
+  return base::File();
 }
 
 int BlinkPlatformImpl::DatabaseDeleteFile(const blink::WebString& vfs_file_name,
diff --git a/content/child/blink_platform_impl.h b/content/child/blink_platform_impl.h
index fcc797e..681033f 100644
--- a/content/child/blink_platform_impl.h
+++ b/content/child/blink_platform_impl.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include "base/compiler_specific.h"
+#include "base/files/file.h"
 #include "base/single_thread_task_runner.h"
 #include "base/timer/timer.h"
 #include "base/trace_event/trace_event.h"
@@ -46,9 +47,8 @@
 
   // Platform methods (partial implementation):
   blink::WebThemeEngine* ThemeEngine() override;
-  blink::Platform::FileHandle DatabaseOpenFile(
-      const blink::WebString& vfs_file_name,
-      int desired_flags) override;
+  base::File DatabaseOpenFile(const blink::WebString& vfs_file_name,
+                              int desired_flags) override;
   int DatabaseDeleteFile(const blink::WebString& vfs_file_name,
                          bool sync_dir) override;
   long DatabaseGetFileAttributes(
diff --git a/content/common/cursors/webcursor_unittest.cc b/content/common/cursors/webcursor_unittest.cc
index d5454d3..614d30f 100644
--- a/content/common/cursors/webcursor_unittest.cc
+++ b/content/common/cursors/webcursor_unittest.cc
@@ -258,7 +258,7 @@
 #if defined(USE_AURA)
 TEST(WebCursorTest, CursorScaleFactor) {
   display::Display display;
-  display.set_device_scale_factor(80.2f);
+  display.set_device_scale_factor(4.2f);
 
   CursorInfo info;
   info.type = WebCursorInfo::kTypeCustom;
@@ -266,10 +266,10 @@
   info.image_scale_factor = 2.0f;
 
   SkImageInfo image_info =
-      SkImageInfo::MakeN32(256, 256, kUnpremul_SkAlphaType);
+      SkImageInfo::MakeN32(128, 128, kUnpremul_SkAlphaType);
   info.custom_image = SkBitmap();
   info.custom_image.setInfo(image_info);
-  info.custom_image.allocN32Pixels(256, 256);
+  info.custom_image.allocN32Pixels(128, 128);
   info.custom_image.eraseColor(0xFFFFFFFF);
 
   WebCursor cursor;
@@ -279,9 +279,9 @@
 #if defined(USE_OZONE)
   // For Ozone cursors, the size of the cursor is capped at 64px, and this is
   // enforce through the calculated scale factor.
-  EXPECT_EQ(0.25f, cursor.GetNativeCursor().device_scale_factor());
+  EXPECT_EQ(0.5f, cursor.GetNativeCursor().device_scale_factor());
 #else
-  EXPECT_EQ(40.1f, cursor.GetNativeCursor().device_scale_factor());
+  EXPECT_EQ(2.1f, cursor.GetNativeCursor().device_scale_factor());
 #endif
 
   // Test that the Display dsf is copied.
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
index bbe5023..21575052 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
@@ -65,6 +65,9 @@
     // A warmed-up connection to a sandboxed service.
     private static SpareChildConnection sSpareSandboxedConnection;
 
+    // A warmed-up connection to a privileged service (network service).
+    private static SpareChildConnection sSparePrivilegedConntection;
+
     // Allocator used for sandboxed services.
     private static ChildConnectionAllocator sSandboxedChildConnectionAllocator;
     private static ChildProcessRanking sSandboxedChildConnectionRanking;
@@ -97,17 +100,20 @@
     // The type of process as determined by the command line.
     private final String mProcessType;
 
+    // Whether the process can use warmed up connection.
+    private final boolean mCanUseWarmUpConnection;
+
     private final ChildProcessLauncher.Delegate mLauncherDelegate =
             new ChildProcessLauncher.Delegate() {
                 @Override
                 public ChildProcessConnection getBoundConnection(
                         ChildConnectionAllocator connectionAllocator,
                         ChildProcessConnection.ServiceCallback serviceCallback) {
-                    if (sSpareSandboxedConnection == null) {
-                        return null;
-                    }
-                    return sSpareSandboxedConnection.getConnection(
-                            connectionAllocator, serviceCallback);
+                    if (!mCanUseWarmUpConnection) return null;
+                    SpareChildConnection spareConnection =
+                            mSandboxed ? sSpareSandboxedConnection : sSparePrivilegedConntection;
+                    if (spareConnection == null) return null;
+                    return spareConnection.getConnection(connectionAllocator, serviceCallback);
                 }
 
                 @Override
@@ -194,8 +200,9 @@
     }
 
     @CalledByNative
-    private static ChildProcessLauncherHelperImpl createAndStart(
-            long nativePointer, String[] commandLine, FileDescriptorInfo[] filesToBeMapped) {
+    private static ChildProcessLauncherHelperImpl createAndStart(long nativePointer,
+            String[] commandLine, FileDescriptorInfo[] filesToBeMapped,
+            boolean canUseWarmUpConnection) {
         assert LauncherThread.runningOnLauncherThread();
         String processType =
                 ContentSwitchUtils.getSwitchValue(commandLine, ContentSwitches.SWITCH_PROCESS_TYPE);
@@ -220,8 +227,8 @@
                 ? new GpuProcessCallback()
                 : null;
 
-        ChildProcessLauncherHelperImpl helper = new ChildProcessLauncherHelperImpl(
-                nativePointer, commandLine, filesToBeMapped, sandboxed, binderCallback);
+        ChildProcessLauncherHelperImpl helper = new ChildProcessLauncherHelperImpl(nativePointer,
+                commandLine, filesToBeMapped, sandboxed, canUseWarmUpConnection, binderCallback);
         helper.start();
         return helper;
     }
@@ -229,24 +236,31 @@
     /**
      * @see {@link ChildProcessLauncherHelper#warmUp(Context)}.
      */
-    public static void warmUp(final Context context) {
+    public static void warmUp(final Context context, boolean sandboxed) {
         assert ThreadUtils.runningOnUiThread();
         LauncherThread.post(new Runnable() {
             @Override
             public void run() {
-                warmUpOnLauncherThread(context);
+                warmUpOnLauncherThread(context, sandboxed);
             }
         });
     }
 
-    private static void warmUpOnLauncherThread(Context context) {
-        if (sSpareSandboxedConnection != null && !sSpareSandboxedConnection.isEmpty()) {
+    private static void warmUpOnLauncherThread(Context context, boolean sandboxed) {
+        SpareChildConnection spareConnection =
+                sandboxed ? sSpareSandboxedConnection : sSparePrivilegedConntection;
+        if (spareConnection != null && !spareConnection.isEmpty()) {
             return;
         }
 
         Bundle serviceBundle = populateServiceBundle(new Bundle());
-        ChildConnectionAllocator allocator = getConnectionAllocator(context, true /* sandboxed */);
-        sSpareSandboxedConnection = new SpareChildConnection(context, allocator, serviceBundle);
+        ChildConnectionAllocator allocator = getConnectionAllocator(context, sandboxed);
+        if (sandboxed) {
+            sSpareSandboxedConnection = new SpareChildConnection(context, allocator, serviceBundle);
+        } else {
+            sSparePrivilegedConntection =
+                    new SpareChildConnection(context, allocator, serviceBundle);
+        }
     }
 
     /**
@@ -367,12 +381,13 @@
     }
 
     private ChildProcessLauncherHelperImpl(long nativePointer, String[] commandLine,
-            FileDescriptorInfo[] filesToBeMapped, boolean sandboxed, IBinder binderCallback) {
+            FileDescriptorInfo[] filesToBeMapped, boolean sandboxed, boolean canUseWarmUpConnection,
+            IBinder binderCallback) {
         assert LauncherThread.runningOnLauncherThread();
 
         mNativeChildProcessLauncherHelper = nativePointer;
         mSandboxed = sandboxed;
-
+        mCanUseWarmUpConnection = canUseWarmUpConnection;
         ChildConnectionAllocator connectionAllocator =
                 getConnectionAllocator(ContextUtils.getApplicationContext(), sandboxed);
         mLauncher = new ChildProcessLauncher(LauncherThread.getHandler(), mLauncherDelegate,
@@ -624,10 +639,10 @@
 
     @VisibleForTesting
     public static ChildProcessLauncherHelperImpl createAndStartForTesting(String[] commandLine,
-            FileDescriptorInfo[] filesToBeMapped, boolean sandboxed, IBinder binderCallback,
-            boolean doSetupConnection) {
-        ChildProcessLauncherHelperImpl launcherHelper = new ChildProcessLauncherHelperImpl(
-                0L, commandLine, filesToBeMapped, sandboxed, binderCallback);
+            FileDescriptorInfo[] filesToBeMapped, boolean sandboxed, boolean canUseWarmUpConnection,
+            IBinder binderCallback, boolean doSetupConnection) {
+        ChildProcessLauncherHelperImpl launcherHelper = new ChildProcessLauncherHelperImpl(0L,
+                commandLine, filesToBeMapped, sandboxed, canUseWarmUpConnection, binderCallback);
         launcherHelper.mLauncher.start(doSetupConnection, true /* queueIfNoFreeConnection */);
         return launcherHelper;
     }
@@ -659,8 +674,10 @@
     }
 
     @VisibleForTesting
-    public static ChildProcessConnection getWarmUpConnectionForTesting() {
-        return sSpareSandboxedConnection == null ? null : sSpareSandboxedConnection.getConnection();
+    public static ChildProcessConnection getWarmUpConnectionForTesting(boolean sandboxed) {
+        SpareChildConnection connection =
+                sandboxed ? sSpareSandboxedConnection : sSparePrivilegedConntection;
+        return connection == null ? null : connection.getConnection();
     }
 
     private static native void nativeSetTerminationInfo(long termiantionInfoPtr,
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ChildProcessLauncherHelper.java b/content/public/android/java/src/org/chromium/content_public/browser/ChildProcessLauncherHelper.java
index 833b1f5..3f66a6f 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/ChildProcessLauncherHelper.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/ChildProcessLauncherHelper.java
@@ -18,9 +18,10 @@
      * Creates a ready to use sandboxed child process. Should be called early during startup so the
      * child process is created while other startup work is happening.
      * @param context the application context used for the connection.
+     * @param sandboxed Whether the child process is sandboxed.
      */
-    public static void warmUp(Context context) {
-        ChildProcessLauncherHelperImpl.warmUp(context);
+    public static void warmUp(Context context, boolean sandboxed) {
+        ChildProcessLauncherHelperImpl.warmUp(context, sandboxed);
     }
 
     /**
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java
index d1c0b8ac..f16b5adb 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java
@@ -213,21 +213,21 @@
                 0, ChildProcessLauncherTestUtils.getConnectionServiceNumber(connection));
     }
 
-    private static void warmUpOnUiThreadBlocking(final Context context) {
+    private static void warmUpOnUiThreadBlocking(final Context context, boolean sandboxed) {
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                ChildProcessLauncherHelperImpl.warmUp(context);
+                ChildProcessLauncherHelperImpl.warmUp(context, sandboxed);
             }
         });
-        ChildProcessConnection connection = getWarmUpConnection();
+        ChildProcessConnection connection = getWarmUpConnection(sandboxed);
         Assert.assertNotNull(connection);
         blockUntilConnected(connection);
     }
 
     private void testWarmUpImpl() {
         Context context = InstrumentationRegistry.getTargetContext();
-        warmUpOnUiThreadBlocking(context);
+        warmUpOnUiThreadBlocking(context, true /* sandboxed */);
 
         Assert.assertEquals(1, getConnectedSandboxedServicesCount());
 
@@ -272,12 +272,12 @@
         Assert.assertEquals(0, getConnectedSandboxedServicesCount());
 
         Context context = InstrumentationRegistry.getTargetContext();
-        warmUpOnUiThreadBlocking(context);
+        warmUpOnUiThreadBlocking(context, true /* sandboxed */);
 
         Assert.assertEquals(1, getConnectedSandboxedServicesCount());
 
         // Crash the warm-up connection before it gets used.
-        ChildProcessConnection connection = getWarmUpConnection();
+        ChildProcessConnection connection = getWarmUpConnection(true /* sandboxed */);
         Assert.assertNotNull(connection);
         connection.crashServiceForTesting();
 
@@ -297,7 +297,7 @@
     @Feature({"ProcessManagement"})
     public void testWarmUpProcessCrashAfterUse() throws RemoteException {
         Context context = InstrumentationRegistry.getTargetContext();
-        warmUpOnUiThreadBlocking(context);
+        warmUpOnUiThreadBlocking(context, true /* sandboxed */);
 
         Assert.assertEquals(1, getConnectedSandboxedServicesCount());
 
@@ -316,6 +316,28 @@
         waitForConnectedSandboxedServicesCount(0);
     }
 
+    // Tests that the warm-up the previleged process connection.
+    @Test
+    @MediumTest
+    @Feature({"ProcessManagement"})
+    public void testWarmUpPrivilegedProcess() throws RemoteException {
+        Assert.assertEquals(0, getConnectedServicesCount());
+
+        Context context = InstrumentationRegistry.getTargetContext();
+        warmUpOnUiThreadBlocking(context, false /* sandboxed */);
+
+        Assert.assertEquals(0, getConnectedSandboxedServicesCount());
+        Assert.assertEquals(1, getConnectedServicesCount());
+
+        // And subsequent process launches should work.
+        ChildProcessLauncherHelperImpl launcher =
+                startChildProcess(BLOCK_UNTIL_SETUP, true /* doSetupConnection */,
+                        false /* sandboxed */, true /* canUseWarmUpConnection */);
+        Assert.assertEquals(1, getConnectedServicesCount());
+        Assert.assertEquals(0, getConnectedSandboxedServicesCount());
+        Assert.assertNotNull(ChildProcessLauncherTestUtils.getConnection(launcher));
+    }
+
     @Test
     @MediumTest
     @Feature({"ProcessManagement"})
@@ -341,6 +363,12 @@
 
     private static ChildProcessLauncherHelperImpl startSandboxedChildProcess(
             int blockingPolicy, final boolean doSetupConnection) {
+        return startChildProcess(blockingPolicy, doSetupConnection, true /* sandboxed */,
+                true /* canUseWarmUpConnection */);
+    }
+
+    private static ChildProcessLauncherHelperImpl startChildProcess(int blockingPolicy,
+            final boolean doSetupConnection, boolean sandboxed, boolean canUseWarmUpConnection) {
         assert doSetupConnection || blockingPolicy != BLOCK_UNTIL_SETUP;
         ChildProcessLauncherHelperImpl launcher =
                 ChildProcessLauncherTestUtils.runOnLauncherAndGetResult(
@@ -348,8 +376,8 @@
                             @Override
                             public ChildProcessLauncherHelperImpl call() {
                                 return ChildProcessLauncherHelperImpl.createAndStartForTesting(
-                                        sProcessWaitArguments, new FileDescriptorInfo[0],
-                                        true /* sandboxed */, null /* binderCallback */,
+                                        sProcessWaitArguments, new FileDescriptorInfo[0], sandboxed,
+                                        canUseWarmUpConnection, null /* binderCallback */,
                                         doSetupConnection);
                             }
                         });
@@ -394,6 +422,16 @@
                 });
     }
 
+    // Returns the number of all connection currently connected.
+    private static int getConnectedServicesCount() {
+        return ChildProcessLauncherTestUtils.runOnLauncherAndGetResult(new Callable<Integer>() {
+            @Override
+            public Integer call() {
+                return ChildProcessLauncherHelperImpl.getConnectedServicesCountForTesting();
+            }
+        });
+    }
+
     // Returns the number of sandboxed connection currently connected,
     private static int getConnectedSandboxedServicesCount() {
         return ChildProcessLauncherTestUtils.runOnLauncherAndGetResult(new Callable<Integer>() {
@@ -463,12 +501,13 @@
         });
     }
 
-    private static ChildProcessConnection getWarmUpConnection() {
+    private static ChildProcessConnection getWarmUpConnection(boolean sandboxed) {
         return ChildProcessLauncherTestUtils.runOnLauncherAndGetResult(
                 new Callable<ChildProcessConnection>() {
                     @Override
                     public ChildProcessConnection call() {
-                        return ChildProcessLauncherHelperImpl.getWarmUpConnectionForTesting();
+                        return ChildProcessLauncherHelperImpl.getWarmUpConnectionForTesting(
+                                sandboxed);
                     }
                 });
     }
diff --git a/content/public/browser/authenticator_request_client_delegate.h b/content/public/browser/authenticator_request_client_delegate.h
index 3c6a600..551c9e3f 100644
--- a/content/public/browser/authenticator_request_client_delegate.h
+++ b/content/public/browser/authenticator_request_client_delegate.h
@@ -41,6 +41,7 @@
     kKeyAlreadyRegistered,
     kSoftPINBlock,
     kHardPINBlock,
+    kAuthenticatorRemovedDuringPINEntry,
   };
 
   AuthenticatorRequestClientDelegate();
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 13f529dd..228843a 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -703,6 +703,10 @@
     "BackgroundMediaRendererHasModerateBinding",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Pre-warm up the network process on browser startup.
+const base::Feature kWarmUpNetworkProcess{"WarmUpNetworkProcess",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls whether the WebNFC API is enabled:
 // https://w3c.github.io/web-nfc/
 const base::Feature kWebNfc{"WebNFC", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index b8c82ac..5f2f523 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -156,6 +156,7 @@
 CONTENT_EXPORT extern const base::Feature
     kBackgroundMediaRendererHasModerateBinding;
 CONTENT_EXPORT extern const base::Feature kHideIncorrectlySizedFullscreenFrames;
+CONTENT_EXPORT extern const base::Feature kWarmUpNetworkProcess;
 CONTENT_EXPORT extern const base::Feature kWebNfc;
 #endif  // defined(OS_ANDROID)
 
diff --git a/content/renderer/gpu_benchmarking_extension.cc b/content/renderer/gpu_benchmarking_extension.cc
index cadc792..63138f0 100644
--- a/content/renderer/gpu_benchmarking_extension.cc
+++ b/content/renderer/gpu_benchmarking_extension.cc
@@ -968,7 +968,7 @@
     return 0.0;
   float y = context.web_view()->VisualViewportOffset().y;
   blink::WebRect rect(0, y, 0, 0);
-  context.render_view_impl()->WidgetClient()->ConvertViewportToWindow(&rect);
+  context.render_view_impl()->GetWidget()->ConvertViewportToWindow(&rect);
   return rect.y;
 }
 
@@ -978,7 +978,7 @@
     return 0.0;
   float x = context.web_view()->VisualViewportOffset().x;
   blink::WebRect rect(x, 0, 0, 0);
-  context.render_view_impl()->WidgetClient()->ConvertViewportToWindow(&rect);
+  context.render_view_impl()->GetWidget()->ConvertViewportToWindow(&rect);
   return rect.x;
 }
 
@@ -988,7 +988,7 @@
     return 0.0;
   float height = context.web_view()->VisualViewportSize().height;
   blink::WebRect rect(0, 0, 0, height);
-  context.render_view_impl()->WidgetClient()->ConvertViewportToWindow(&rect);
+  context.render_view_impl()->GetWidget()->ConvertViewportToWindow(&rect);
   return rect.height;
 }
 
@@ -998,7 +998,7 @@
     return 0.0;
   float width = context.web_view()->VisualViewportSize().width;
   blink::WebRect rect(0, 0, width, 0);
-  context.render_view_impl()->WidgetClient()->ConvertViewportToWindow(&rect);
+  context.render_view_impl()->GetWidget()->ConvertViewportToWindow(&rect);
   return rect.width;
 }
 
diff --git a/content/renderer/loader/resource_dispatcher_unittest.cc b/content/renderer/loader/resource_dispatcher_unittest.cc
index 16284c4c..75e8c99 100644
--- a/content/renderer/loader/resource_dispatcher_unittest.cc
+++ b/content/renderer/loader/resource_dispatcher_unittest.cc
@@ -18,7 +18,6 @@
 #include "base/memory/shared_memory.h"
 #include "base/process/process_handle.h"
 #include "base/run_loop.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "content/common/appcache_interfaces.h"
 #include "content/public/common/content_features.h"
@@ -68,8 +67,7 @@
 }
 
 std::string GetRequestPeerContextBody(TestRequestPeer::Context* context) {
-  if (base::FeatureList::IsEnabled(blink::features::kResourceLoadViaDataPipe) &&
-      context->body_handle) {
+  if (context->body_handle) {
     context->data += ReadOneChunk(&context->body_handle);
   }
   return context->data;
@@ -78,13 +76,10 @@
 }  // namespace
 
 // Sets up the message sender override for the unit test.
-class ResourceDispatcherTest : public testing::TestWithParam<bool>,
+class ResourceDispatcherTest : public testing::Test,
                                public network::mojom::URLLoaderFactory {
  public:
-  ResourceDispatcherTest() : dispatcher_(new ResourceDispatcher()) {
-    scoped_feature_list_.InitWithFeatureState(
-        blink::features::kResourceLoadViaDataPipe, IsResourceLoadViaDataPipe());
-  }
+  ResourceDispatcherTest() : dispatcher_(new ResourceDispatcher()) {}
 
   ~ResourceDispatcherTest() override {
     dispatcher_.reset();
@@ -162,10 +157,7 @@
     return options;
   }
 
-  static bool IsResourceLoadViaDataPipe() { return GetParam(); }
-
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   std::vector<std::pair<network::mojom::URLLoaderRequest,
                         network::mojom::URLLoaderClientPtr>>
       loader_and_clients_;
@@ -173,12 +165,8 @@
   std::unique_ptr<ResourceDispatcher> dispatcher_;
 };
 
-INSTANTIATE_TEST_SUITE_P(ResourceDispatcherTestP,
-                         ResourceDispatcherTest,
-                         ::testing::Bool());
-
 // Tests the generation of unique request ids.
-TEST_P(ResourceDispatcherTest, MakeRequestID) {
+TEST_F(ResourceDispatcherTest, MakeRequestID) {
   int first_id = ResourceDispatcher::MakeRequestID();
   int second_id = ResourceDispatcher::MakeRequestID();
 
@@ -230,22 +218,15 @@
     }
 
     void OnReceivedData(std::unique_ptr<ReceivedData> data) override {
-      data_.append(data->payload(), data->length());
+      NOTREACHED();
     }
+
     void OnTransferSizeUpdated(int transfer_size_diff) override {}
 
     void OnCompletedRequest(
         const network::URLLoaderCompletionStatus& status) override {
       original_peer_->OnReceivedResponse(response_info_);
-      if (!data_.empty()) {
-        DCHECK(!body_handle_);
-        original_peer_->OnReceivedData(
-            std::make_unique<FixedReceivedData>(data_.data(), data_.size()));
-      }
-      if (body_handle_) {
-        DCHECK(data_.empty());
-        original_peer_->OnStartLoadingResponseBody(std::move(body_handle_));
-      }
+      original_peer_->OnStartLoadingResponseBody(std::move(body_handle_));
       original_peer_->OnCompletedRequest(status);
     }
     scoped_refptr<base::TaskRunner> GetTaskRunner() override {
@@ -255,7 +236,6 @@
    private:
     std::unique_ptr<RequestPeer> original_peer_;
     network::ResourceResponseInfo response_info_;
-    std::string data_;
     mojo::ScopedDataPipeConsumerHandle body_handle_;
 
     DISALLOW_COPY_AND_ASSIGN(WrapperPeer);
@@ -265,7 +245,7 @@
   DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherDelegate);
 };
 
-TEST_P(ResourceDispatcherTest, DelegateTest) {
+TEST_F(ResourceDispatcherTest, DelegateTest) {
   std::unique_ptr<network::ResourceRequest> request(CreateResourceRequest());
   TestRequestPeer::Context peer_context;
   StartAsync(std::move(request), nullptr, &peer_context);
@@ -312,7 +292,7 @@
   EXPECT_TRUE(peer_context.complete);
 }
 
-TEST_P(ResourceDispatcherTest, CancelDuringCallbackWithWrapperPeer) {
+TEST_F(ResourceDispatcherTest, CancelDuringCallbackWithWrapperPeer) {
   std::unique_ptr<network::ResourceRequest> request(CreateResourceRequest());
   TestRequestPeer::Context peer_context;
   StartAsync(std::move(request), nullptr, &peer_context);
@@ -359,11 +339,11 @@
   EXPECT_FALSE(peer_context.complete);
 }
 
-TEST_P(ResourceDispatcherTest, Cookies) {
+TEST_F(ResourceDispatcherTest, Cookies) {
   // FIXME
 }
 
-TEST_P(ResourceDispatcherTest, SerializedPostData) {
+TEST_F(ResourceDispatcherTest, SerializedPostData) {
   // FIXME
 }
 
@@ -389,7 +369,7 @@
 };
 
 // TODO(simonjam): Enable this when 10829031 lands.
-TEST_P(TimeConversionTest, DISABLED_ProperlyInitialized) {
+TEST_F(TimeConversionTest, DISABLED_ProperlyInitialized) {
   network::ResourceResponseHead response_head;
   response_head.request_start = base::TimeTicks::FromInternalValue(5);
   response_head.response_start = base::TimeTicks::FromInternalValue(15);
@@ -408,7 +388,7 @@
             response_info().load_timing.connect_timing.connect_start);
 }
 
-TEST_P(TimeConversionTest, PartiallyInitialized) {
+TEST_F(TimeConversionTest, PartiallyInitialized) {
   network::ResourceResponseHead response_head;
   response_head.request_start = base::TimeTicks::FromInternalValue(5);
   response_head.response_start = base::TimeTicks::FromInternalValue(15);
@@ -420,7 +400,7 @@
             response_info().load_timing.connect_timing.dns_start);
 }
 
-TEST_P(TimeConversionTest, NotInitialized) {
+TEST_F(TimeConversionTest, NotInitialized) {
   network::ResourceResponseHead response_head;
 
   PerformTest(response_head);
@@ -479,7 +459,7 @@
   TestRequestPeer::Context peer_context_;
 };
 
-TEST_P(CompletionTimeConversionTest, NullCompletionTimestamp) {
+TEST_F(CompletionTimeConversionTest, NullCompletionTimestamp) {
   const auto remote_request_start =
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(4);
 
@@ -488,7 +468,7 @@
   EXPECT_EQ(base::TimeTicks(), completion_time());
 }
 
-TEST_P(CompletionTimeConversionTest, RemoteRequestStartIsUnavailable) {
+TEST_F(CompletionTimeConversionTest, RemoteRequestStartIsUnavailable) {
   base::TimeTicks begin = base::TimeTicks::Now();
 
   const auto remote_completion_time =
@@ -501,7 +481,7 @@
   EXPECT_LE(completion_time(), end);
 }
 
-TEST_P(CompletionTimeConversionTest, Convert) {
+TEST_F(CompletionTimeConversionTest, Convert) {
   const auto remote_request_start =
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(4);
 
diff --git a/content/renderer/loader/url_loader_client_impl.cc b/content/renderer/loader/url_loader_client_impl.cc
index 255e7a6..6d68c378 100644
--- a/content/renderer/loader/url_loader_client_impl.cc
+++ b/content/renderer/loader/url_loader_client_impl.cc
@@ -13,7 +13,6 @@
 #include "content/public/common/url_utils.h"
 #include "content/public/renderer/content_renderer_client.h"
 #include "content/renderer/loader/resource_dispatcher.h"
-#include "content/renderer/loader/url_response_body_consumer.h"
 #include "net/url_request/redirect_info.h"
 #include "services/network/public/cpp/features.h"
 #include "third_party/blink/public/common/features.h"
@@ -155,15 +154,10 @@
       url_loader_client_binding_(this),
       weak_factory_(this) {}
 
-URLLoaderClientImpl::~URLLoaderClientImpl() {
-  if (body_consumer_)
-    body_consumer_->Cancel();
-}
+URLLoaderClientImpl::~URLLoaderClientImpl() = default;
 
 void URLLoaderClientImpl::SetDefersLoading() {
   is_deferred_ = true;
-  if (body_consumer_)
-    body_consumer_->SetDefersLoading();
 }
 
 void URLLoaderClientImpl::UnsetDefersLoading() {
@@ -182,9 +176,9 @@
   bool has_completion_message = false;
   base::WeakPtr<URLLoaderClientImpl> weak_this = weak_factory_.GetWeakPtr();
   // First, dispatch all messages excluding the followings:
-  //  - response body (dispatched by |body_consumer_|)
-  //  - transfer size change (dispatched later)
-  //  - completion (dispatched by |body_consumer_| or dispatched later)
+  //  - transfer size change
+  //  - completion
+  // These two types of messages are dispatched later.
   for (size_t index = 0; index < messages.size(); ++index) {
     if (messages[index]->IsCompletionMessage()) {
       // The completion message arrives at the end of the message queue.
@@ -224,15 +218,6 @@
     }
   }
 
-  if (body_consumer_) {
-    // When we have |body_consumer_|, the completion message is dispatched by
-    // it, not by this object.
-    DCHECK(!has_completion_message);
-    // Dispatch the response body.
-    body_consumer_->UnsetDefersLoading();
-    return;
-  }
-
   // Dispatch the completion message.
   if (has_completion_message) {
     DCHECK_GT(messages.size(), 0u);
@@ -265,7 +250,6 @@
     const net::RedirectInfo& redirect_info,
     const network::ResourceResponseHead& response_head) {
   DCHECK(!has_received_response_head_);
-  DCHECK(!body_consumer_);
   if (base::FeatureList::IsEnabled(network::features::kNetworkService) &&
       !bypass_redirect_checks_ &&
       !IsRedirectSafe(last_loaded_url_, redirect_info.new_url)) {
@@ -317,26 +301,10 @@
 
 void URLLoaderClientImpl::OnStartLoadingResponseBody(
     mojo::ScopedDataPipeConsumerHandle body) {
-  DCHECK(!body_consumer_);
   DCHECK(has_received_response_head_);
   DCHECK(!has_received_response_body_);
   has_received_response_body_ = true;
 
-  if (!base::FeatureList::IsEnabled(
-          blink::features::kResourceLoadViaDataPipe) &&
-      !pass_response_pipe_to_dispatcher_) {
-    body_consumer_ = new URLResponseBodyConsumer(
-        request_id_, resource_dispatcher_, std::move(body), task_runner_);
-
-    if (NeedsStoringMessage()) {
-      body_consumer_->SetDefersLoading();
-      return;
-    }
-
-    body_consumer_->OnReadable(MOJO_RESULT_OK);
-    return;
-  }
-
   if (NeedsStoringMessage()) {
     StoreAndDispatch(
         std::make_unique<DeferredOnStartLoadingResponseBody>(std::move(body)));
@@ -351,26 +319,13 @@
   has_received_complete_ = true;
 
   // Dispatch completion status to the ResourceDispatcher.
-  //
-  // Non-ResourceLoadViaDataPipe: Call ResourceDispatcher::OnRequestComplete
-  // only when body doesn't exist since |body_consumer_| will call
-  // ResrouceDispatcher::OnRequestComplete() when body exists.
-  // ResourceLoadViaDataPipe: always go into this path since we no longer use
-  // |body_consumer_| for transferring the body.
-  if (!body_consumer_) {
-    // Except for errors, there must always be a response's body.
-    DCHECK(has_received_response_body_ || status.error_code != net::OK);
-    if (NeedsStoringMessage()) {
-      StoreAndDispatch(std::make_unique<DeferredOnComplete>(status));
-    } else {
-      resource_dispatcher_->OnRequestComplete(request_id_, status);
-    }
-    return;
+  // Except for errors, there must always be a response's body.
+  DCHECK(has_received_response_body_ || status.error_code != net::OK);
+  if (NeedsStoringMessage()) {
+    StoreAndDispatch(std::make_unique<DeferredOnComplete>(status));
+  } else {
+    resource_dispatcher_->OnRequestComplete(request_id_, status);
   }
-
-  DCHECK(
-      !base::FeatureList::IsEnabled(blink::features::kResourceLoadViaDataPipe));
-  body_consumer_->OnComplete(status);
 }
 
 bool URLLoaderClientImpl::NeedsStoringMessage() const {
diff --git a/content/renderer/loader/url_loader_client_impl.h b/content/renderer/loader/url_loader_client_impl.h
index d8cec43..c7bc4a1 100644
--- a/content/renderer/loader/url_loader_client_impl.h
+++ b/content/renderer/loader/url_loader_client_impl.h
@@ -96,12 +96,6 @@
   void StoreAndDispatch(std::unique_ptr<DeferredMessage> message);
   void OnConnectionClosed();
 
-  // Non-ResourceLoadViaDataPipe:
-  // Used for reading the response body from the data pipe passed on
-  // OnStartLoadingResponseBody() and passing the data to corresponding
-  // RequestPeer.
-  scoped_refptr<URLResponseBodyConsumer> body_consumer_;
-
   std::vector<std::unique_ptr<DeferredMessage>> deferred_messages_;
   const int request_id_;
   bool has_received_response_head_ = false;
diff --git a/content/renderer/loader/url_loader_client_impl_unittest.cc b/content/renderer/loader/url_loader_client_impl_unittest.cc
index dafb307..f2177896 100644
--- a/content/renderer/loader/url_loader_client_impl_unittest.cc
+++ b/content/renderer/loader/url_loader_client_impl_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 #include "base/run_loop.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "content/renderer/loader/navigation_response_override_parameters.h"
 #include "content/renderer/loader/resource_dispatcher.h"
@@ -18,7 +17,6 @@
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 
@@ -39,8 +37,7 @@
 }
 
 std::string GetRequestPeerContextBody(TestRequestPeer::Context* context) {
-  if (base::FeatureList::IsEnabled(blink::features::kResourceLoadViaDataPipe) &&
-      context->body_handle) {
+  if (context->body_handle) {
     context->data += ReadOneChunk(&context->body_handle);
   }
   return context->data;
@@ -48,13 +45,10 @@
 
 }  // namespace
 
-class URLLoaderClientImplTest : public ::testing::TestWithParam<bool>,
+class URLLoaderClientImplTest : public ::testing::Test,
                                 public network::mojom::URLLoaderFactory {
  protected:
   URLLoaderClientImplTest() : dispatcher_(new ResourceDispatcher()) {
-    scoped_feature_list_.InitWithFeatureState(
-        blink::features::kResourceLoadViaDataPipe, IsResourceLoadViaDataPipe());
-
     auto request = std::make_unique<network::ResourceRequest>();
     // Set request context type to fetch so that ResourceDispatcher doesn't
     // install MimeSniffingThrottle, which makes URLLoaderThrottleLoader
@@ -95,8 +89,6 @@
     NOTREACHED();
   }
 
-  static bool IsResourceLoadViaDataPipe() { return GetParam(); }
-
   static MojoCreateDataPipeOptions DataPipeOptions() {
     MojoCreateDataPipeOptions options;
     options.struct_size = sizeof(MojoCreateDataPipeOptions);
@@ -107,18 +99,13 @@
   }
 
   base::test::ScopedTaskEnvironment task_environment_;
-  base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<ResourceDispatcher> dispatcher_;
   TestRequestPeer::Context request_peer_context_;
   int request_id_ = 0;
   network::mojom::URLLoaderClientPtr url_loader_client_;
 };
 
-INSTANTIATE_TEST_SUITE_P(URLLoaderClientImplTestP,
-                         URLLoaderClientImplTest,
-                         ::testing::Bool());
-
-TEST_P(URLLoaderClientImplTest, OnReceiveResponse) {
+TEST_F(URLLoaderClientImplTest, OnReceiveResponse) {
   network::ResourceResponseHead response_head;
 
   url_loader_client_->OnReceiveResponse(response_head);
@@ -128,7 +115,7 @@
   EXPECT_TRUE(request_peer_context_.received_response);
 }
 
-TEST_P(URLLoaderClientImplTest, ResponseBody) {
+TEST_F(URLLoaderClientImplTest, ResponseBody) {
   network::ResourceResponseHead response_head;
 
   url_loader_client_->OnReceiveResponse(response_head);
@@ -150,7 +137,7 @@
   EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
 }
 
-TEST_P(URLLoaderClientImplTest, OnReceiveRedirect) {
+TEST_F(URLLoaderClientImplTest, OnReceiveRedirect) {
   network::ResourceResponseHead response_head;
   net::RedirectInfo redirect_info;
 
@@ -161,7 +148,7 @@
   EXPECT_EQ(1, request_peer_context_.seen_redirects);
 }
 
-TEST_P(URLLoaderClientImplTest, OnReceiveCachedMetadata) {
+TEST_F(URLLoaderClientImplTest, OnReceiveCachedMetadata) {
   network::ResourceResponseHead response_head;
   std::vector<uint8_t> metadata;
   metadata.push_back('a');
@@ -177,7 +164,7 @@
   EXPECT_EQ('a', request_peer_context_.cached_metadata[0]);
 }
 
-TEST_P(URLLoaderClientImplTest, OnTransferSizeUpdated) {
+TEST_F(URLLoaderClientImplTest, OnTransferSizeUpdated) {
   network::ResourceResponseHead response_head;
 
   url_loader_client_->OnReceiveResponse(response_head);
@@ -191,7 +178,7 @@
   EXPECT_EQ(8, request_peer_context_.total_encoded_data_length);
 }
 
-TEST_P(URLLoaderClientImplTest, OnCompleteWithResponseBody) {
+TEST_F(URLLoaderClientImplTest, OnCompleteWithResponseBody) {
   network::ResourceResponseHead response_head;
   network::URLLoaderCompletionStatus status;
 
@@ -225,7 +212,7 @@
 // Due to the lack of ordering guarantee, it is possible that the response body
 // bytes arrives after the completion message. URLLoaderClientImpl should
 // restore the order.
-TEST_P(URLLoaderClientImplTest, OnCompleteShouldBeTheLastMessage) {
+TEST_F(URLLoaderClientImplTest, OnCompleteShouldBeTheLastMessage) {
   network::ResourceResponseHead response_head;
   network::URLLoaderCompletionStatus status;
 
@@ -236,17 +223,8 @@
   url_loader_client_->OnComplete(status);
 
   base::RunLoop().RunUntilIdle();
-  if (IsResourceLoadViaDataPipe()) {
-    // ResourceLoadViaDataPipe: We don't guarantee that the order between
-    // finishing to send the body and OnComplete().
-    EXPECT_TRUE(request_peer_context_.received_response);
-    EXPECT_TRUE(request_peer_context_.complete);
-  } else {
-    // Non-ResourceLoadViaDataPipe: OnComplete() won't be delivered until all of
-    // the body has been read.
-    EXPECT_TRUE(request_peer_context_.received_response);
-    EXPECT_FALSE(request_peer_context_.complete);
-  }
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_TRUE(request_peer_context_.complete);
 
   uint32_t size = 5;
   MojoResult result = data_pipe.producer_handle->WriteData(
@@ -256,20 +234,9 @@
 
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
-
-  // ResourceLoadViaDataPipe: Everything has finished at this point.
-  if (IsResourceLoadViaDataPipe())
-    return;
-
-  EXPECT_FALSE(request_peer_context_.complete);
-
-  data_pipe.producer_handle.reset();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
-  EXPECT_TRUE(request_peer_context_.complete);
 }
 
-TEST_P(URLLoaderClientImplTest, CancelOnReceiveResponse) {
+TEST_F(URLLoaderClientImplTest, CancelOnReceiveResponse) {
   request_peer_context_.cancel_on_receive_response = true;
 
   network::ResourceResponseHead response_head;
@@ -291,41 +258,7 @@
   EXPECT_TRUE(request_peer_context_.cancelled);
 }
 
-TEST_P(URLLoaderClientImplTest, CancelOnReceiveData) {
-  // ResourceLoadViaDataPipe: doesn't use OnReceivedData().
-  if (IsResourceLoadViaDataPipe())
-    return;
-
-  request_peer_context_.cancel_on_receive_data = true;
-
-  network::ResourceResponseHead response_head;
-  network::URLLoaderCompletionStatus status;
-
-  mojo::DataPipe data_pipe(DataPipeOptions());
-  uint32_t size = 5;
-  MojoResult result = data_pipe.producer_handle->WriteData(
-      "hello", &size, MOJO_WRITE_DATA_FLAG_NONE);
-  ASSERT_EQ(MOJO_RESULT_OK, result);
-  EXPECT_EQ(5u, size);
-
-  url_loader_client_->OnReceiveResponse(response_head);
-  url_loader_client_->OnStartLoadingResponseBody(
-      std::move(data_pipe.consumer_handle));
-  url_loader_client_->OnComplete(status);
-
-  EXPECT_FALSE(request_peer_context_.received_response);
-  EXPECT_EQ("", request_peer_context_.data);
-  EXPECT_FALSE(request_peer_context_.complete);
-  EXPECT_FALSE(request_peer_context_.cancelled);
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(request_peer_context_.received_response);
-  EXPECT_EQ("hello", request_peer_context_.data);
-  EXPECT_FALSE(request_peer_context_.complete);
-  EXPECT_TRUE(request_peer_context_.cancelled);
-}
-
-TEST_P(URLLoaderClientImplTest, Defer) {
+TEST_F(URLLoaderClientImplTest, Defer) {
   network::ResourceResponseHead response_head;
   network::URLLoaderCompletionStatus status;
 
@@ -354,7 +287,7 @@
   EXPECT_TRUE(request_peer_context_.complete);
 }
 
-TEST_P(URLLoaderClientImplTest, DeferWithResponseBody) {
+TEST_F(URLLoaderClientImplTest, DeferWithResponseBody) {
   network::ResourceResponseHead response_head;
   network::URLLoaderCompletionStatus status;
 
@@ -395,7 +328,7 @@
 
 // As "transfer size update" message is handled specially in the implementation,
 // we have a separate test.
-TEST_P(URLLoaderClientImplTest, DeferWithTransferSizeUpdated) {
+TEST_F(URLLoaderClientImplTest, DeferWithTransferSizeUpdated) {
   network::ResourceResponseHead response_head;
   network::URLLoaderCompletionStatus status;
 
@@ -439,7 +372,7 @@
   EXPECT_EQ(4, request_peer_context_.total_encoded_data_length);
 }
 
-TEST_P(URLLoaderClientImplTest, SetDeferredDuringFlushingDeferredMessage) {
+TEST_F(URLLoaderClientImplTest, SetDeferredDuringFlushingDeferredMessage) {
   request_peer_context_.defer_on_redirect = true;
 
   net::RedirectInfo redirect_info;
@@ -501,7 +434,7 @@
   EXPECT_FALSE(request_peer_context_.cancelled);
 }
 
-TEST_P(URLLoaderClientImplTest,
+TEST_F(URLLoaderClientImplTest,
        SetDeferredDuringFlushingDeferredMessageOnTransferSizeUpdated) {
   request_peer_context_.defer_on_transfer_size_updated = true;
 
@@ -547,46 +480,4 @@
   EXPECT_FALSE(request_peer_context_.cancelled);
 }
 
-TEST_P(URLLoaderClientImplTest, CancelOnReceiveDataWhileFlushing) {
-  // ResourceLoadViaDataPipe: doesn't use OnReceiveData() so this test is
-  // useless.
-  if (IsResourceLoadViaDataPipe())
-    return;
-  request_peer_context_.cancel_on_receive_data = true;
-  dispatcher_->SetDefersLoading(request_id_, true);
-
-  network::ResourceResponseHead response_head;
-  network::URLLoaderCompletionStatus status;
-
-  mojo::DataPipe data_pipe(DataPipeOptions());
-  uint32_t size = 5;
-  MojoResult result = data_pipe.producer_handle->WriteData(
-      "hello", &size, MOJO_WRITE_DATA_FLAG_NONE);
-  ASSERT_EQ(MOJO_RESULT_OK, result);
-  EXPECT_EQ(5u, size);
-
-  url_loader_client_->OnReceiveResponse(response_head);
-  url_loader_client_->OnStartLoadingResponseBody(
-      std::move(data_pipe.consumer_handle));
-  url_loader_client_->OnComplete(status);
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(request_peer_context_.received_response);
-  EXPECT_EQ("", request_peer_context_.data);
-  EXPECT_FALSE(request_peer_context_.complete);
-  EXPECT_FALSE(request_peer_context_.cancelled);
-
-  dispatcher_->SetDefersLoading(request_id_, false);
-  EXPECT_FALSE(request_peer_context_.received_response);
-  EXPECT_EQ("", request_peer_context_.data);
-  EXPECT_FALSE(request_peer_context_.complete);
-  EXPECT_FALSE(request_peer_context_.cancelled);
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(request_peer_context_.received_response);
-  EXPECT_EQ("hello", request_peer_context_.data);
-  EXPECT_FALSE(request_peer_context_.complete);
-  EXPECT_TRUE(request_peer_context_.cancelled);
-}
-
 }  // namespace content
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index 5a39571..a8c9a5e 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -420,8 +420,6 @@
 
   // Called when the body data stream is detached from the reader side.
   void CancelBodyStreaming();
-  // We can optimize the handling of data URLs in most cases.
-  bool CanHandleDataURLRequestLocally(const WebURLRequest& request) const;
 
   void OnBodyAvailable(MojoResult, const mojo::HandleSignalsState&);
   void OnBodyHasBeenRead(uint32_t read_bytes);
@@ -623,8 +621,6 @@
     defers_loading_ = NOT_DEFERRING;
 
     if (body_watcher_.IsWatching()) {
-      DCHECK(base::FeatureList::IsEnabled(
-          blink::features::kResourceLoadViaDataPipe));
       body_watcher_.ArmOrNotify();
     }
   }
@@ -654,10 +650,6 @@
   report_raw_headers_ = request.ReportRawHeaders();
   pass_response_pipe_to_client_ = request.PassResponsePipeToClient();
 
-  // TODO(https://crbug.com/923779): Remove this once after we can confirm it's
-  // working well.
-  CHECK(!CanHandleDataURLRequestLocally(request));
-
   std::unique_ptr<NavigationResponseOverrideParameters> response_override;
   if (request.GetExtraData()) {
     RequestExtraData* extra_data =
@@ -947,26 +939,6 @@
   Cancel();
 }
 
-bool WebURLLoaderImpl::Context::CanHandleDataURLRequestLocally(
-    const WebURLRequest& request) const {
-  if (!request.Url().ProtocolIs(url::kDataScheme))
-    return false;
-
-  // The fast paths for data URL, Start() and HandleDataURL(), don't support
-  // the PassResponsePipeToClient option.
-  if (request.PassResponsePipeToClient())
-    return false;
-
-  // Data url requests from object tags may need to be intercepted as streams
-  // and so need to be sent to the browser.
-  if (request.GetRequestContext() == blink::mojom::RequestContextType::OBJECT)
-    return false;
-
-  DCHECK_EQ(network::mojom::RequestContextFrameType::kNone,
-            request.GetFrameType());
-  return true;
-}
-
 void WebURLLoaderImpl::Context::OnBodyAvailable(
     MojoResult,
     const mojo::HandleSignalsState&) {
@@ -1036,8 +1008,6 @@
 
 void WebURLLoaderImpl::Context::MaybeCompleteRequest() {
   if (!completion_status_.has_value() || body_handle_.is_valid()) {
-    DCHECK(base::FeatureList::IsEnabled(
-        blink::features::kResourceLoadViaDataPipe));
     // OnCompletedRequest() hasn't been called yet or the body didn't reach to
     // the end.
     return;
@@ -1098,9 +1068,7 @@
 
 void WebURLLoaderImpl::RequestPeerImpl::OnReceivedData(
     std::unique_ptr<ReceivedData> data) {
-  if (discard_body_)
-    return;
-  context_->OnReceivedData(std::move(data));
+  NOTREACHED();
 }
 
 void WebURLLoaderImpl::RequestPeerImpl::OnTransferSizeUpdated(
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 18749bd4..32cd5dd 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1354,7 +1354,7 @@
   // Close(). The RenderViewImpl has a RenderWidget already, but not a
   // WebFrameWidget, which is now attached here.
   auto* web_frame_widget = blink::WebFrameWidget::CreateForMainFrame(
-      render_view->WidgetClient(), web_frame);
+      render_view->GetWidget(), web_frame);
   render_view->AttachWebFrameWidget(web_frame_widget);
   // TODO(crbug.com/419087): This was added in 6ccadf770766e89c3 to prevent an
   // empty ScreenInfo, but the WebView has already been created and initialized
@@ -1511,7 +1511,7 @@
     // Close(). The RenderViewImpl has a RenderWidget already, but not a
     // WebFrameWidget, which is now attached here.
     auto* web_frame_widget = blink::WebFrameWidget::CreateForMainFrame(
-        render_view->WidgetClient(), web_frame);
+        render_view->GetWidget(), web_frame);
     render_view->AttachWebFrameWidget(web_frame_widget);
     // TODO(crbug.com/419087): This was added in 6ccadf770766e89c3 to prevent
     // an empty ScreenInfo, but the WebView has already been created and
@@ -3293,10 +3293,8 @@
       !FrameMsg_Navigate_Type::IsSameDocument(common_params.navigation_type));
   if (ShouldIgnoreCommitNavigation(commit_params)) {
     browser_side_navigation_pending_url_ = GURL();
-    if (IsPerNavigationMojoInterfaceEnabled())
-      navigation_client_impl_.reset();
-    else
-      std::move(callback).Run(blink::mojom::CommitResult::Aborted);
+    AbortCommitNavigation(std::move(callback),
+                          blink::mojom::CommitResult::Aborted);
     return;
   }
 
@@ -3439,8 +3437,6 @@
     std::unique_ptr<WebNavigationParams> navigation_params) {
   if (ShouldIgnoreCommitNavigation(commit_params)) {
     browser_side_navigation_pending_url_ = GURL();
-    if (IsPerNavigationMojoInterfaceEnabled())
-      navigation_client_impl_.reset();
     return;
   }
 
@@ -3585,11 +3581,8 @@
   if (!ShouldDisplayErrorPageForFailedLoad(error_code, common_params.url)) {
     // The browser expects this frame to be loading an error page. Inform it
     // that the load stopped.
-    if (callback) {
-      std::move(callback).Run(blink::mojom::CommitResult::Aborted);
-    } else {
-      navigation_client_impl_.reset();
-    }
+    AbortCommitNavigation(std::move(callback),
+                          blink::mojom::CommitResult::Aborted);
     Send(new FrameHostMsg_DidStopLoading(routing_id_));
     browser_side_navigation_pending_ = false;
     browser_side_navigation_pending_url_ = GURL();
@@ -3607,18 +3600,12 @@
       // either, as the frame has already been populated with something
       // unrelated to this navigation failure. In that case, just send a stop
       // IPC to the browser to unwind its state, and leave the frame as-is.
-      if (callback) {
-        std::move(callback).Run(blink::mojom::CommitResult::Aborted);
-      } else {
-        navigation_client_impl_.reset();
-      }
+      AbortCommitNavigation(std::move(callback),
+                            blink::mojom::CommitResult::Aborted);
       Send(new FrameHostMsg_DidStopLoading(routing_id_));
     } else {
-      if (callback) {
-        std::move(callback).Run(blink::mojom::CommitResult::Ok);
-      } else {
-        navigation_client_impl_.reset();
-      }
+      AbortCommitNavigation(std::move(callback),
+                            blink::mojom::CommitResult::Ok);
     }
     browser_side_navigation_pending_ = false;
     browser_side_navigation_pending_url_ = GURL();
@@ -7542,4 +7529,21 @@
       std::move(fallback_factory));
 }
 
+void RenderFrameImpl::AbortCommitNavigation(
+    mojom::FrameNavigationControl::CommitNavigationCallback callback,
+    blink::mojom::CommitResult reason) {
+  DCHECK(callback || IsPerNavigationMojoInterfaceEnabled());
+  // The callback will trigger
+  // RenderFrameHostImpl::OnCrossDocumentCommitProcessed() as will the interface
+  // disconnection. Note: We are using the callback and not the flag to
+  // determine if NavigationClient::CommitNavigation was used, because in
+  // certain cases we use the old path even when the flag is on (e.g. some
+  // interstitials).
+  if (callback) {
+    std::move(callback).Run(reason);
+  } else {
+    navigation_client_impl_.reset();
+  }
+}
+
 }  // namespace content
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index fe02f31..49bd9bd 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1431,6 +1431,14 @@
       mojom::NavigationClient::CommitFailedNavigationCallback
           per_navigation_mojo_interface_callback);
 
+  // Ignores the navigation commit and stop its processing in the RenderFrame.
+  // This will drop the NavigationRequest in the RenderFrameHost.
+  // Note: This is only meant to be used before building the DocumentState.
+  // Commit abort and navigation end is handled by it afterwards.
+  void AbortCommitNavigation(
+      mojom::FrameNavigationControl::CommitNavigationCallback callback,
+      blink::mojom::CommitResult reason);
+
   // Stores the WebLocalFrame we are associated with.  This is null from the
   // constructor until BindToFrame() is called, and it is null after
   // FrameDetached() is called until destruction (which is asynchronous in the
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index fc40f8a..b4236f8d 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -2584,7 +2584,7 @@
        ConverViewportToWindowWithoutZoomForDSF) {
   SetDeviceScaleFactor(2.f);
   blink::WebRect rect(20, 10, 200, 100);
-  view()->WidgetClient()->ConvertViewportToWindow(&rect);
+  view()->GetWidget()->ConvertViewportToWindow(&rect);
   EXPECT_EQ(20, rect.x);
   EXPECT_EQ(10, rect.y);
   EXPECT_EQ(200, rect.width);
@@ -2653,7 +2653,7 @@
   SetDeviceScaleFactor(1.f);
   {
     blink::WebRect rect(20, 10, 200, 100);
-    view()->WidgetClient()->ConvertViewportToWindow(&rect);
+    view()->GetWidget()->ConvertViewportToWindow(&rect);
     EXPECT_EQ(20, rect.x);
     EXPECT_EQ(10, rect.y);
     EXPECT_EQ(200, rect.width);
@@ -2663,7 +2663,7 @@
   SetDeviceScaleFactor(2.f);
   {
     blink::WebRect rect(20, 10, 200, 100);
-    view()->WidgetClient()->ConvertViewportToWindow(&rect);
+    view()->GetWidget()->ConvertViewportToWindow(&rect);
     EXPECT_EQ(10, rect.x);
     EXPECT_EQ(5, rect.y);
     EXPECT_EQ(100, rect.width);
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 80fa551..e7d4026 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1564,7 +1564,7 @@
   // passed to the creation of the WebFrameWidget or the main RenderFrame.
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
-  WidgetClient()->SetShowFPSCounter(
+  render_widget_->SetShowFPSCounter(
       command_line.HasSwitch(cc::switches::kShowFPSCounter));
 }
 
@@ -1757,13 +1757,6 @@
   return true;
 }
 
-blink::WebWidgetClient* RenderViewImpl::WidgetClient() {
-  // TODO(ajwong): Remove this API and force clients to get the
-  // WebWidgetClient through GetWidget().
-  // https://crbug.com/545684
-  return render_widget_;
-}
-
 // blink::WebLocalFrameClient
 // -----------------------------------------------------
 
@@ -1814,13 +1807,13 @@
 #endif
 
 void RenderViewImpl::ConvertViewportToWindowViaWidget(blink::WebRect* rect) {
-  WidgetClient()->ConvertViewportToWindow(rect);
+  render_widget_->ConvertViewportToWindow(rect);
 }
 
 gfx::RectF RenderViewImpl::ElementBoundsInWindow(
     const blink::WebElement& element) {
   blink::WebRect bounding_box_in_window = element.BoundsInViewport();
-  WidgetClient()->ConvertViewportToWindow(&bounding_box_in_window);
+  render_widget_->ConvertViewportToWindow(&bounding_box_in_window);
   return gfx::RectF(bounding_box_in_window);
 }
 
@@ -1837,7 +1830,7 @@
 
   blink::WebSize tmp_size = webview()->ContentsPreferredMinimumSize();
   blink::WebRect tmp_rect(0, 0, tmp_size.width, tmp_size.height);
-  WidgetClient()->ConvertViewportToWindow(&tmp_rect);
+  render_widget_->ConvertViewportToWindow(&tmp_rect);
   gfx::Size size(tmp_rect.width, tmp_rect.height);
   if (size == preferred_size_)
     return;
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 9e388f03..2df769cd 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -296,10 +296,6 @@
   void ConvertViewportToWindowViaWidget(blink::WebRect* rect) override;
   gfx::RectF ElementBoundsInWindow(const blink::WebElement& element) override;
 
-  // Can be overridden by web tests to inject their own WebWidgetClient instead
-  // of RenderWidget.
-  virtual blink::WebWidgetClient* WidgetClient();
-
   // Please do not add your stuff randomly to the end here. If there is an
   // appropriate section, add it there. If not, there are some random functions
   // nearer to the top you can add it to.
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 0f18c93..b7efdc4 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -585,6 +585,8 @@
   // Take a reference on behalf of the RenderThread.  This will be balanced
   // when we receive WidgetMsg_Close.
   AddRef();
+
+  initialized_ = true;
 }
 
 void RenderWidget::ApplyEmulatedScreenMetricsForPopupWidget(
@@ -1207,14 +1209,16 @@
 }
 
 void RenderWidget::ScheduleAnimation() {
+  CHECK(initialized_);
+  CHECK(!closed_);
   // TODO(crbug.com/939262): This shouldn't be null. That would mean we're
   // somehow using RenderWidget after it has closed.
   if (!layer_tree_view_) {
     // Determine if this object is for a child or main frame (it should be one
     // of them!)
-    CHECK(for_child_local_root_frame_);
-    CHECK(delegate_);
-    CHECK(!for_child_local_root_frame_ && !delegate_);
+    CHECK(!for_child_local_root_frame_);
+    CHECK(!delegate_);
+    CHECK(for_child_local_root_frame_ || delegate_);
   }
   // This call is not needed in single thread mode for tests without a
   // scheduler, but they override this method in order to schedule a synchronous
@@ -1910,6 +1914,7 @@
     delegate()->DidCloseWidget();
   // Note the ACK is a control message going to the RenderProcessHost.
   RenderThread::Get()->Send(new WidgetHostMsg_Close_ACK(routing_id()));
+  closed_ = true;
 }
 
 void RenderWidget::CloseWebWidget() {
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index d79f871..4bbc7c8 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -930,6 +930,10 @@
   // ImeEventGuard. We keep track of the outermost one, and update it as needed.
   ImeEventGuard* ime_event_guard_;
 
+  // TODO(crbug.com/939262): Track usage of an uninitialized or closed
+  // RenderWidget.
+  bool initialized_ = false;
+  bool closed_ = false;
   // True if we have requested this widget be closed.  No more messages will
   // be sent, except for a Close.
   bool closing_ = false;
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index ff3761b..34b2c4da 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -107,6 +107,7 @@
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/sqlite/sqlite3.h"
+#include "ui/gl/buildflags.h"
 #include "url/gurl.h"
 
 #if defined(OS_MACOSX)
@@ -523,12 +524,12 @@
 
 //------------------------------------------------------------------------------
 
-Platform::FileHandle RendererBlinkPlatformImpl::DatabaseOpenFile(
+base::File RendererBlinkPlatformImpl::DatabaseOpenFile(
     const WebString& vfs_file_name,
     int desired_flags) {
   base::File file;
   GetWebDatabaseHost().OpenFile(vfs_file_name.Utf16(), desired_flags, &file);
-  return file.TakePlatformFile();
+  return file;
 }
 
 int RendererBlinkPlatformImpl::DatabaseDeleteFile(
@@ -983,11 +984,15 @@
 RendererBlinkPlatformImpl::CreateWebGPUGraphicsContext3DProvider(
     const blink::WebURL& top_document_web_url,
     blink::Platform::GraphicsInfo* gl_info) {
+#if !BUILDFLAG(USE_DAWN)
+  return nullptr;
+#else
   scoped_refptr<gpu::GpuChannelHost> gpu_channel_host(
       RenderThreadImpl::current()->EstablishGpuChannelSync());
   if (!gpu_channel_host) {
     std::string error_message(
-        "OffscreenContext Creation failed, GpuChannelHost creation failed");
+        "WebGPUGraphicsContext3DProvider creation failed, GpuChannelHost "
+        "creation failed");
     gl_info->error_message = WebString::FromUTF8(error_message);
     return nullptr;
   }
@@ -1014,6 +1019,7 @@
           ws::command_buffer_metrics::ContextType::WEBGPU));
   return std::make_unique<WebGraphicsContext3DProviderImpl>(
       std::move(provider));
+#endif
 }
 
 //------------------------------------------------------------------------------
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index b0ca0373..4881660 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -108,9 +108,8 @@
       override;
   std::unique_ptr<blink::WebStorageNamespace> CreateSessionStorageNamespace(
       base::StringPiece namespace_id) override;
-  blink::Platform::FileHandle DatabaseOpenFile(
-      const blink::WebString& vfs_file_name,
-      int desired_flags) override;
+  base::File DatabaseOpenFile(const blink::WebString& vfs_file_name,
+                              int desired_flags) override;
   int DatabaseDeleteFile(const blink::WebString& vfs_file_name,
                          bool sync_dir) override;
   long DatabaseGetFileAttributes(
diff --git a/content/renderer/service_worker/service_worker_timeout_timer.cc b/content/renderer/service_worker/service_worker_timeout_timer.cc
index 20b9292..65ea52eb 100644
--- a/content/renderer/service_worker/service_worker_timeout_timer.cc
+++ b/content/renderer/service_worker/service_worker_timeout_timer.cc
@@ -72,9 +72,8 @@
 
 void ServiceWorkerTimeoutTimer::Start() {
   CHECK(!timer_.IsRunning());
-  CHECK(idle_time_.is_null());
   // |idle_callback_| will be invoked if no event happens in |kIdleDelay|.
-  if (!HasInflightEvent())
+  if (!HasInflightEvent() && idle_time_.is_null())
     idle_time_ = tick_clock_->NowTicks() + kIdleDelay;
   timer_.Start(FROM_HERE, kUpdateInterval,
                base::BindRepeating(&ServiceWorkerTimeoutTimer::UpdateStatus,
diff --git a/content/renderer/service_worker/service_worker_timeout_timer.h b/content/renderer/service_worker/service_worker_timeout_timer.h
index c6ccb31..f16f2399 100644
--- a/content/renderer/service_worker/service_worker_timeout_timer.h
+++ b/content/renderer/service_worker/service_worker_timeout_timer.h
@@ -63,7 +63,9 @@
                             const base::TickClock* tick_clock);
   ~ServiceWorkerTimeoutTimer();
 
-  // Starts the timer.
+  // Starts the timer. This may also update |idle_time_| if there was no
+  // activities (i.e., StartEvent()/EndEvent() or StayAwakeToken creation)
+  // on the timer before.
   void Start();
 
   // StartEvent() should be called at the beginning of an event. It returns an
diff --git a/content/renderer/service_worker/service_worker_timeout_timer_unittest.cc b/content/renderer/service_worker/service_worker_timeout_timer_unittest.cc
index 253903cd..68f602a 100644
--- a/content/renderer/service_worker/service_worker_timeout_timer_unittest.cc
+++ b/content/renderer/service_worker/service_worker_timeout_timer_unittest.cc
@@ -167,6 +167,40 @@
   EXPECT_FALSE(is_idle);
 }
 
+// Tests whether idle_time_ won't be updated in Start() when there was an
+// event. The timeline is something like:
+// [StartEvent] [EndEvent]
+//       +----------+
+//                  ^
+//                  +-- idle_time_ --+
+//                                   v
+//                           [TimerStart]         [UpdateStatus]
+//                                 +-- kUpdateInterval --+
+// In the first UpdateStatus() the idle callback should be triggered.
+TEST_F(ServiceWorkerTimeoutTimerTest, EventFinishedBeforeStart) {
+  bool is_idle = false;
+  ServiceWorkerTimeoutTimer timer(CreateReceiverWithCalledFlag(&is_idle),
+                                  task_runner()->GetMockTickClock());
+  // Start and finish an event before starting the timer.
+  int event_id = timer.StartEvent(base::DoNothing());
+  task_runner()->FastForwardBy(base::TimeDelta::FromSeconds(1));
+  timer.EndEvent(event_id);
+
+  // Move the time ticks to almost before |idle_time_| so that |idle_callback|
+  // will get called at the first update check.
+  task_runner()->FastForwardBy(ServiceWorkerTimeoutTimer::kIdleDelay -
+                               base::TimeDelta::FromSeconds(1));
+
+  timer.Start();
+
+  // Make sure the timer calls UpdateStatus().
+  task_runner()->FastForwardBy(ServiceWorkerTimeoutTimer::kUpdateInterval +
+                               base::TimeDelta::FromSeconds(1));
+  // |idle_callback| should be fired because enough time passed since the last
+  // event.
+  EXPECT_TRUE(is_idle);
+}
+
 TEST_F(ServiceWorkerTimeoutTimerTest, EventTimer) {
   ServiceWorkerTimeoutTimer timer(base::DoNothing(),
                                   task_runner()->GetMockTickClock());
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestUtils.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestUtils.java
index 0e76862..fbac2de 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestUtils.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestUtils.java
@@ -59,7 +59,8 @@
             @Override
             public ChildProcessLauncherHelperImpl call() {
                 return ChildProcessLauncherHelperImpl.createAndStartForTesting(commandLine,
-                        filesToBeMapped, sandboxed, null /* binderCallback */, doSetupConnection);
+                        filesToBeMapped, sandboxed, true /* canUseWarmUpConnection */,
+                        null /* binderCallback */, doSetupConnection);
             }
         });
     }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 07b60600..578fe1dd 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1507,6 +1507,7 @@
     "../browser/indexed_db/mock_mojo_indexed_db_database_callbacks.cc",
     "../browser/indexed_db/mock_mojo_indexed_db_database_callbacks.h",
     "../browser/indexed_db/scopes/disjoint_range_lock_manager_unittest.cc",
+    "../browser/indexed_db/scopes/leveldb_scopes_coding_unittest.cc",
     "../browser/indexed_db/scopes/scopes_lock_manager_unittest.cc",
     "../browser/loader/cross_site_document_resource_handler_unittest.cc",
     "../browser/loader/data_pipe_to_source_stream_unittest.cc",
@@ -1874,7 +1875,10 @@
   }
 
   if (is_mac) {
-    sources += [ "../renderer/sandbox_mac_v2_unittest.mm" ]
+    sources += [
+      "../browser/media/now_playing_info_center_notifier_unittest.cc",
+      "../renderer/sandbox_mac_v2_unittest.mm",
+    ]
   }
 
   if (use_atk) {
@@ -2129,6 +2133,7 @@
       "//third_party/mozilla",
       "//third_party/ocmock",
       "//ui/accelerated_widget_mac",
+      "//ui/base/now_playing:test_support",
     ]
     libs = [
       "Carbon.framework",
diff --git a/content/test/gpu/gpu_tests/gpu_helper.py b/content/test/gpu/gpu_tests/gpu_helper.py
new file mode 100644
index 0000000..9433f67
--- /dev/null
+++ b/content/test/gpu/gpu_tests/gpu_helper.py
@@ -0,0 +1,87 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+
+def _ParseANGLEGpuVendorString(device_string):
+  if not device_string:
+    return None
+  # ANGLE's device (renderer) string is of the form:
+  # "ANGLE (vendor_string, renderer_string, gl_version profile)"
+  # This function will be used to get the first value in the tuple
+  match = re.search(r'ANGLE \((.*), .*, .*\)', device_string)
+  if match:
+    return match.group(1)
+  else:
+    return None
+
+
+def _GetANGLEGpuDeviceId(device_string):
+  if not device_string:
+    return None
+  # ANGLE's device (renderer) string is of the form:
+  # "ANGLE (vendor_string, renderer_string, gl_version profile)"
+  # This function will be used to get the second value in the tuple
+  match = re.search(r'ANGLE \(.*, (.*), .*\)', device_string)
+  if match:
+    return match.group(1)
+  else:
+    return None
+
+
+def GetGpuVendorString(gpu_info):
+  if gpu_info:
+    primary_gpu = gpu_info.devices[0]
+    if primary_gpu:
+      vendor_string = primary_gpu.vendor_string
+      angle_vendor_string = _ParseANGLEGpuVendorString(
+        primary_gpu.device_string)
+      vendor_id = primary_gpu.vendor_id
+      if vendor_id == 0x10DE:
+        return 'nvidia'
+      elif vendor_id == 0x1002:
+        return 'amd'
+      elif vendor_id == 0x8086:
+        return 'intel'
+      elif angle_vendor_string:
+        return angle_vendor_string.lower()
+      elif vendor_string:
+        return vendor_string.split(' ')[0].lower()
+  return 'unknown_gpu'
+
+
+def GetGpuDeviceId(gpu_info):
+  if gpu_info:
+    primary_gpu = gpu_info.devices[0]
+    if primary_gpu:
+      return (
+          primary_gpu.device_id
+          or _GetANGLEGpuDeviceId(
+              primary_gpu.device_string)
+          or primary_gpu.device_string)
+  return 0
+
+
+def GetANGLERenderer(gpu_info):
+  if gpu_info and gpu_info.aux_attributes:
+    gl_renderer = gpu_info.aux_attributes.get('gl_renderer')
+    if gl_renderer and 'ANGLE' in gl_renderer:
+      if 'Direct3D11' in gl_renderer:
+        return 'd3d11'
+      elif 'Direct3D9' in gl_renderer:
+        return 'd3d9'
+      elif 'OpenGL ES' in gl_renderer:
+        return 'opengles'
+      elif 'OpenGL' in gl_renderer:
+        return 'opengl'
+      elif 'Vulkan' in gl_renderer:
+        return 'vulkan'
+  return 'no_angle'
+
+
+def GetCommandDecoder(gpu_info):
+  if gpu_info and gpu_info.aux_attributes and \
+      gpu_info.aux_attributes.get('passthrough_cmd_decoder', False):
+    return 'passthrough'
+  return 'no_passthrough'
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test.py b/content/test/gpu/gpu_tests/gpu_integration_test.py
index 5f90d22f..eeea2c9 100644
--- a/content/test/gpu/gpu_tests/gpu_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_integration_test.py
@@ -10,6 +10,7 @@
 
 from gpu_tests import exception_formatter
 from gpu_tests import gpu_test_expectations
+from gpu_tests import gpu_helper
 
 _START_BROWSER_RETRIES = 3
 
@@ -374,6 +375,49 @@
     raise NotImplementedError
 
   @classmethod
+  def GenerateTags(cls, finder_options, possible_browser):
+    # If no expectations file paths are returned from cls.ExpectationsFiles()
+    # then an empty list will be returned from this function. If tags are
+    # returned and there are no expectations files, then Typ will raise
+    # an exception.
+    if not cls.ExpectationsFiles():
+      return []
+    with possible_browser.BrowserSession(finder_options) as browser:
+      return cls.GetPlatformTags(browser)
+
+  @classmethod
+  def GetPlatformTags(cls, browser):
+    """This function will take a Browser instance as an argument.
+    It will call the super classes implementation of GetPlatformTags() to get
+    a list of tags. Then it will add the gpu vendor, gpu device id,
+    angle renderer, and command line decoder tags to that list before
+    returning it.
+    """
+    tags = super(GpuIntegrationTest, cls).GetPlatformTags(browser)
+    system_info = browser.GetSystemInfo()
+    if system_info:
+      gpu_info = system_info.gpu
+      gpu_vendor = gpu_helper.GetGpuVendorString(gpu_info)
+      gpu_device_id = gpu_helper.GetGpuDeviceId(gpu_info)
+      # The gpu device id tag will contain both the vendor and device id
+      # separated by a '-'.
+      try:
+        # If the device id is an integer then it will be added as
+        # a hexadecimal to the tag
+        gpu_device_tag = '%s-0x%x' % (gpu_vendor, gpu_device_id)
+      except TypeError:
+        # if the device id is not an integer it will be added as
+        # a string to the tag.
+        gpu_device_tag = '%s-%s' % (gpu_vendor, gpu_device_id)
+      angle_renderer = gpu_helper.GetANGLERenderer(gpu_info)
+      cmd_decoder = gpu_helper.GetCommandDecoder(gpu_info)
+      # all spaces in the tag will be replaced by '-', and all letters will
+      # be converted to its lower case form.
+      tags.extend([tag.lower().replace(' ', '-') for tag in [
+          gpu_vendor, gpu_device_tag, angle_renderer, cmd_decoder]])
+    return tags
+
+  @classmethod
   def _EnsureTabIsAvailable(cls):
     try:
       cls.tab = cls.browser.tabs[0]
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py b/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
index eeb5821..d81211a 100644
--- a/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
+++ b/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
@@ -10,6 +10,7 @@
 import mock
 
 from telemetry.testing import browser_test_runner
+from telemetry.internal.platform import system_info
 
 from gpu_tests import path_util
 from gpu_tests import gpu_integration_test
@@ -17,12 +18,152 @@
 path_util.AddDirToPathIfNeeded(path_util.GetChromiumSrcDir(), 'tools', 'perf')
 from chrome_telemetry_build import chromium_config
 
+VENDOR_NVIDIA = 0x10DE
+VENDOR_AMD = 0x1002
+VENDOR_INTEL = 0x8086
+
+VENDOR_STRING_IMAGINATION = 'Imagination Technologies'
+DEVICE_STRING_SGX = 'PowerVR SGX 554'
+
+class MockPlatform(object):
+  def __init__(self, os_name, os_version_name=None):
+    self.os_name = os_name
+    self.os_version_name = os_version_name
+
+  def GetOSName(self):
+    return self.os_name
+
+  def GetOSVersionName(self):
+    return self.os_version_name
+
+
+class MockBrowser(object):
+  def __init__(self, platform, gpu='', device='', vendor_string='',
+               device_string='', browser_type=None, gl_renderer=None,
+               passthrough=False):
+    self.platform = platform
+    self.browser_type = browser_type
+    sys_info = {
+      'model_name': '',
+      'gpu': {
+        'devices': [
+          {'vendor_id': gpu, 'device_id': device,
+           'vendor_string': vendor_string, 'device_string': device_string},
+        ],
+       'aux_attributes': {'passthrough_cmd_decoder': passthrough}
+      }
+    }
+    if gl_renderer:
+      sys_info['gpu']['aux_attributes']['gl_renderer'] = gl_renderer
+    self.system_info = system_info.SystemInfo.FromDict(sys_info)
+
+  def GetSystemInfo(self):
+    return self.system_info
+
+  def __enter__(self):
+    return self
+
+  def __exit__(self, *args):
+    pass
+
+
+class MockArgs(object):
+  pass
+
+
+class MockAbstractGpuTestClass(gpu_integration_test.GpuIntegrationTest):
+
+  @classmethod
+  def GenerateGpuTests(cls, options):
+    pass
+
+  def RunActualGpuTest(self, test_path, *args):
+    pass
+
+  @classmethod
+  def _CreateExpectations(cls):
+    pass
+
+
+class MockTestCaseWithoutExpectationsFile(MockAbstractGpuTestClass):
+  pass
+
+
+class MockTestCaseWithExpectationsFile(MockAbstractGpuTestClass):
+
+  @classmethod
+  def ExpectationsFiles(cls):
+    return ['example_test_expectations.txt']
+
+
+class MockPossibleBrowser(object):
+
+  def __init__(self, browser=None):
+    self._returned_browser = browser
+
+  def BrowserSession(self, options):
+    del options
+    return self._returned_browser
+
 
 class GpuIntegrationTestUnittest(unittest.TestCase):
   def setUp(self):
     self._test_state = {}
     self._test_result = {}
 
+  def testWithoutExpectationsFilesGenerateTagsReturnsEmptyList(self):
+    # we need to make sure that GenerateTags() returns an empty list if
+    # there are no expectations files returned from ExpectationsFiles() or
+    # else Typ will raise an exception
+    args = MockArgs()
+    possible_browser = MockPossibleBrowser()
+    self.assertFalse(MockTestCaseWithoutExpectationsFile.GenerateTags(
+        args, possible_browser))
+
+  def testGenerateNvidiaExampleTags(self):
+    args = MockArgs()
+    platform = MockPlatform('mac', 'mojave')
+    browser = MockBrowser(
+        platform, VENDOR_NVIDIA, 0x1cb3, browser_type='release',
+        gl_renderer='ANGLE Direct3D9')
+    possible_browser = MockPossibleBrowser(browser)
+    self.assertEqual(
+        set(MockTestCaseWithExpectationsFile.GenerateTags(
+            args, possible_browser)),
+        set(['mac', 'mojave', 'release', 'nvidia', 'nvidia-0x1cb3',
+             'd3d9', 'no_passthrough']))
+
+  def testGenerateVendorTagUsingVendorString(self):
+    args = MockArgs()
+    platform = MockPlatform('mac', 'mojave')
+    browser = MockBrowser(
+        platform, browser_type='release',
+        gl_renderer='ANGLE OpenGL ES', passthrough=True,
+        vendor_string=VENDOR_STRING_IMAGINATION,
+        device_string=DEVICE_STRING_SGX)
+    possible_browser = MockPossibleBrowser(browser)
+    self.assertEqual(
+        set(MockTestCaseWithExpectationsFile.GenerateTags(
+            args, possible_browser)),
+        set(['mac', 'mojave', 'release', 'imagination',
+             'imagination-powervr-sgx-554',
+             'opengles', 'passthrough']))
+
+  def testGenerateVendorTagUsingDeviceString(self):
+    args = MockArgs()
+    platform = MockPlatform('mac', 'mojave')
+    browser = MockBrowser(
+        platform, browser_type='release',
+        vendor_string='illegal vendor string',
+        device_string='ANGLE (Imagination, Triangle Monster 3000, 1.0)')
+    possible_browser = MockPossibleBrowser(browser)
+    self.assertEqual(
+        set(MockTestCaseWithExpectationsFile.GenerateTags(
+            args, possible_browser)),
+        set(['mac', 'mojave', 'release', 'imagination',
+             'imagination-triangle-monster-3000',
+             'no_angle', 'no_passthrough']))
+
   def testSimpleIntegrationTest(self):
     self._RunIntegrationTest(
       'simple_integration_unittest',
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index c740efb..b264a5e0 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -1349,6 +1349,14 @@
     # Basic failures that need to be investigated on multiple devices
     self.Fail('conformance2/glsl3/vector-dynamic-indexing-swizzled-lvalue.html',
         ['android'], bug=709351)
+    # Video uploads to some texture formats new in WebGL 2.0 are
+    # failing.
+    self.Fail('conformance2/textures/video/' +
+        'tex-2d-rg8ui-rg_integer-unsigned_byte.html',
+        ['android'], bug=906735)
+    self.Fail('conformance2/textures/video/' +
+        'tex-2d-rgb8ui-rgb_integer-unsigned_byte.html',
+        ['android'], bug=906735)
 
     # Qualcomm (Pixel 2) failures
     self.Fail('conformance/extensions/webgl-compressed-texture-astc.html',
@@ -1357,6 +1365,9 @@
         ['android', 'qualcomm'], bug=906739)
     self.Fail('conformance2/glsl3/compare-structs-containing-arrays.html',
         ['android', 'qualcomm'], bug=906742)
+    self.Fail('conformance2/textures/video/' +
+        'tex-2d-rgba8ui-rgba_integer-unsigned_byte.html',
+        ['android', 'qualcomm'], bug=906735)
     self.Fail('conformance2/textures/misc/tex-new-formats.html',
         ['android', 'qualcomm'], bug=906740)
     self.Fail('conformance2/textures/misc/copy-texture-image-luma-format.html',
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index fb6e43f..9e89229e 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -59,8 +59,6 @@
         ['win', 'mac', 'linux', 'android'])
     self.Skip('WebglExtension_EXT_disjoint_timer_query',
         ['android'], bug=808744)
-    self.Fail('WebglExtension_EXT_disjoint_timer_query',
-        ['linux', 'intel'], bug=867675)
     self.Skip('WebglExtension_KHR_parallel_shader_compile',
         ['no_passthrough'], bug=849576)
 
@@ -143,7 +141,7 @@
         'tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html',
         ['passthrough', 'opengl'], bug=2952) # angle bug ID
 
-    # Passthrough command decoder / OpenGL / Intel
+    # Intel graphics driver issue. Passed on 25.20.100.6471
     self.Fail('conformance/glsl/constructors/glsl-construct-mat2.html',
         ['passthrough', 'opengl', 'intel'], bug=665521)
 
@@ -256,8 +254,6 @@
         ['win', 'intel'], bug=825338)
     self.Flaky('conformance/glsl/misc/shader-with-non-reserved-words.html',
         ['win', 'intel'], bug=929009)
-    self.Fail('conformance/rendering/rendering-stencil-large-viewport.html',
-        ['win', 'intel', 'd3d11'], bug=782317)
 
     # This is an OpenGL driver bug on Intel platform and it is fixed in
     # Intel Driver 25.20.100.6444.
@@ -312,10 +308,6 @@
     self.Skip('conformance/glsl/misc/large-loop-compile.html',
         ['win', 'd3d9'], bug=674572)
 
-    # WIN / D3D9 / Intel failures
-    self.Fail('conformance/ogles/GL/cos/cos_001_to_006.html',
-        ['win', 'intel', 'd3d9'], bug=540538)
-
     # WIN / OpenGL / NVIDIA failures
     self.Fail('conformance/limits/gl-max-texture-dimensions.html',
         ['win', ('nvidia', 0x1cb3), 'opengl', 'passthrough'], bug=715001)
@@ -341,45 +333,8 @@
     self.Flaky('conformance/*', ['win', 'amd', 'opengl'], bug=582083)
 
     # Win / OpenGL / Intel HD 530 / 630 failures
-    self.Fail('conformance/canvas/draw-webgl-to-canvas-test.html',
-        ['win10', 'intel', 'opengl'], bug=680797)
-    self.Fail('conformance/extensions/angle-instanced-arrays.html',
-        ['win10', 'intel', 'opengl'], bug=680797)
-    # self.Fail('conformance/extensions/ext-sRGB.html',
-    #     ['win10', 'intel', 'opengl', 'no_passthrough'], bug=680797)
-    self.Fail('conformance/extensions/ext-shader-texture-lod.html',
-        ['win10', 'intel', 'opengl', 'no_passthrough'], bug=680797)
-    self.Fail('conformance/extensions/oes-texture-float-with-canvas.html',
-        ['win10', 'intel', 'opengl', 'no_passthrough'], bug=680797)
-    self.Fail('conformance/extensions/oes-texture-half-float.html',
-        ['win10', 'intel', 'opengl', 'no_passthrough'], bug=680797)
-    self.Fail('conformance/extensions/oes-texture-half-float-with-canvas.html',
-        ['win10', 'intel', 'opengl', 'no_passthrough'], bug=680797)
-    self.Fail('conformance/glsl/bugs/' +
-        'array-of-struct-with-int-first-position.html',
-        ['win10', 'intel', 'opengl'], bug=680797)
-    self.Fail('conformance/glsl/bugs/constant-precision-qualifier.html',
-        ['win10', 'intel', 'opengl'], bug=680797)
-    self.Fail('conformance/glsl/matrices/matrix-compound-multiply.html',
-        ['win10', 'intel', 'opengl'], bug=680797)
     self.Flaky('conformance/glsl/variables/gl-pointcoord.html',
         ['win10', 'intel', 'opengl'], bug=854100)
-    self.Fail('conformance/more/conformance/webGLArrays.html',
-        ['win10', 'intel', 'opengl'], bug=680797)
-    self.Fail('conformance/ogles/GL/struct/struct_049_to_056.html',
-        ['win10', 'intel', 'opengl'], bug=680797)
-    self.Fail('conformance/renderbuffers/framebuffer-state-restoration.html',
-        ['win10', 'intel', 'opengl'], bug=680797)
-    self.Fail('conformance/rendering/draw-with-changing-start-vertex-bug.html',
-        ['win10', 'intel', 'opengl'], bug=680797)
-    self.Fail('conformance/textures/image_bitmap_from_canvas/' +
-        'tex-2d-rgb-rgb-unsigned_short_5_6_5.html',
-        ['win10', 'intel', 'opengl'], bug=680797)
-    self.Fail('conformance/textures/image_bitmap_from_canvas/' +
-        'tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html',
-        ['win10', 'intel', 'opengl'], bug=680797)
-    self.Fail('conformance/textures/misc/texture-fakeblack.html',
-        ['win10', 'intel', 'opengl', 'no_passthrough'], bug=680797)
 
     # Win / Intel / Passthrough command decoder
     self.Flaky('conformance/renderbuffers/framebuffer-state-restoration.html',
diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc
index 0ec76675..6e713de 100644
--- a/content/test/navigation_simulator_impl.cc
+++ b/content/test/navigation_simulator_impl.cc
@@ -312,7 +312,6 @@
                            ? render_frame_host->frame_tree_node()
                            : web_contents->GetMainFrame()->frame_tree_node()),
       request_(nullptr),
-      original_url_(original_url),
       navigation_url_(original_url),
       initial_method_("GET"),
       browser_initiated_(browser_initiated),
@@ -362,7 +361,6 @@
   CHECK(render_frame_host_);
   CHECK_EQ(frame_tree_node_, request_->frame_tree_node());
   state_ = STARTED;
-  original_url_ = request->commit_params().original_url;
   navigation_url_ = handle->GetURL();
   // |remote_endpoint_| cannot be inferred from the request.
   // |initial_method_| cannot be set after the request has started.
@@ -1191,7 +1189,7 @@
   std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params =
       std::make_unique<FrameHostMsg_DidCommitProvisionalLoad_Params>();
   params->url = navigation_url_;
-  params->original_request_url = original_url_;
+  params->original_request_url = navigation_url_;
   params->referrer = referrer_;
   params->contents_mime_type = contents_mime_type_;
   params->transition = transition_;
diff --git a/content/test/navigation_simulator_impl.h b/content/test/navigation_simulator_impl.h
index c8ef0c6..d71d1c8 100644
--- a/content/test/navigation_simulator_impl.h
+++ b/content/test/navigation_simulator_impl.h
@@ -220,7 +220,6 @@
 
   // Note: additional parameters to modify the navigation should be properly
   // initialized (if needed) in InitializeFromStartedRequest.
-  GURL original_url_;
   GURL navigation_url_;
   net::IPEndPoint remote_endpoint_;
   bool is_signed_exchange_inner_response_ = false;
diff --git a/device/fido/ctap_get_assertion_request.h b/device/fido/ctap_get_assertion_request.h
index 84982ee3..6008db26 100644
--- a/device/fido/ctap_get_assertion_request.h
+++ b/device/fido/ctap_get_assertion_request.h
@@ -98,7 +98,7 @@
   std::string client_data_json_;
   std::array<uint8_t, kClientDataHashLength> client_data_hash_;
   UserVerificationRequirement user_verification_ =
-      UserVerificationRequirement::kPreferred;
+      UserVerificationRequirement::kDiscouraged;
   bool user_presence_required_ = true;
 
   base::Optional<std::vector<PublicKeyCredentialDescriptor>> allow_list_;
diff --git a/device/fido/ctap_make_credential_request.h b/device/fido/ctap_make_credential_request.h
index 12b72d5..2554344 100644
--- a/device/fido/ctap_make_credential_request.h
+++ b/device/fido/ctap_make_credential_request.h
@@ -106,7 +106,7 @@
   PublicKeyCredentialUserEntity user_;
   PublicKeyCredentialParams public_key_credential_params_;
   UserVerificationRequirement user_verification_ =
-      UserVerificationRequirement::kPreferred;
+      UserVerificationRequirement::kDiscouraged;
   AuthenticatorAttachment authenticator_attachment_ =
       AuthenticatorAttachment::kAny;
   bool resident_key_required_ = false;
diff --git a/device/fido/fido_authenticator.cc b/device/fido/fido_authenticator.cc
index 093fe35..deba83fa 100644
--- a/device/fido/fido_authenticator.cc
+++ b/device/fido/fido_authenticator.cc
@@ -45,6 +45,17 @@
   NOTREACHED();
 }
 
+AuthenticatorSupportedOptions::ClientPinAvailability
+FidoAuthenticator::WillNeedPINToMakeCredential(const CtapMakeCredentialRequest&
+    request) {
+  return AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported;
+}
+
+bool FidoAuthenticator::WillNeedPINToGetAssertion(const
+    CtapGetAssertionRequest& request) {
+  return false;
+}
+
 void FidoAuthenticator::Reset(ResetCallback callback) {
   std::move(callback).Run(CtapDeviceResponseCode::kCtap1ErrInvalidCommand,
                           base::nullopt);
diff --git a/device/fido/fido_authenticator.h b/device/fido/fido_authenticator.h
index bfef7d1..07a3a91f 100644
--- a/device/fido/fido_authenticator.h
+++ b/device/fido/fido_authenticator.h
@@ -100,6 +100,18 @@
                          const std::string& new_pin,
                          pin::KeyAgreementResponse& peer_key,
                          SetPINCallback callback);
+  // WillNeedPINToMakeCredential returns what type of PIN intervention will be
+  // needed to serve
+  // the given request on this authenticator.
+  //   |kNotSupported|: no PIN involved.
+  //   |kSupportedButPinNotSet|: will need to set a new PIN.
+  //   |kSupportedAndPinSet|: will need to prompt for an existing PIN.
+  virtual AuthenticatorSupportedOptions::ClientPinAvailability
+    WillNeedPINToMakeCredential( const CtapMakeCredentialRequest& request);
+  // WillNeedPINToGetAssertion returns whether a PIN prompt will be needed to
+  // serve the given request on this authenticator.
+  virtual bool WillNeedPINToGetAssertion(const CtapGetAssertionRequest&
+      request);
   // Reset triggers a reset operation on the authenticator. This erases all
   // stored resident keys and any configured PIN.
   virtual void Reset(ResetCallback callback);
diff --git a/device/fido/fido_device_authenticator.cc b/device/fido/fido_device_authenticator.cc
index fae86f8..2dd96b84 100644
--- a/device/fido/fido_device_authenticator.cc
+++ b/device/fido/fido_device_authenticator.cc
@@ -59,24 +59,6 @@
                                              MakeCredentialCallback callback) {
   DCHECK(device_->SupportedProtocolIsInitialized())
       << "InitializeAuthenticator() must be called first.";
-  DCHECK(Options());
-
-  // When PIN support is enabled, the mapping from Webauthn's ternary user-
-  // verification preference to CTAP2's binary option is done inside the request
-  // handler instead.
-  if (!base::FeatureList::IsEnabled(device::kWebAuthPINSupport) ||
-      request.user_verification() == UserVerificationRequirement::kPreferred) {
-    if (Options()->user_verification_availability ==
-        AuthenticatorSupportedOptions::UserVerificationAvailability::
-            kSupportedAndConfigured) {
-      request.SetUserVerification(UserVerificationRequirement::kRequired);
-    } else {
-      request.SetUserVerification(UserVerificationRequirement::kDiscouraged);
-    }
-  }
-
-  // TODO(martinkr): Change FidoTasks to take all request parameters by const
-  // reference, so we can avoid copying these from the RequestHandler.
   task_ = std::make_unique<MakeCredentialTask>(
       device_.get(), std::move(request), std::move(callback));
 }
@@ -85,26 +67,6 @@
                                            GetAssertionCallback callback) {
   DCHECK(device_->SupportedProtocolIsInitialized())
       << "InitializeAuthenticator() must be called first.";
-  DCHECK(Options());
-  const bool pin_support =
-      base::FeatureList::IsEnabled(device::kWebAuthPINSupport);
-
-  // Update the request to the "effective" user verification requirement.
-  // https://w3c.github.io/webauthn/#effective-user-verification-requirement-for-assertion
-  if (!pin_support ||
-      request.user_verification() == UserVerificationRequirement::kPreferred) {
-    if (Options()->user_verification_availability ==
-            AuthenticatorSupportedOptions::UserVerificationAvailability::
-                kSupportedAndConfigured ||
-        (pin_support && Options()->client_pin_availability ==
-                            AuthenticatorSupportedOptions::
-                                ClientPinAvailability::kSupportedAndPinSet)) {
-      request.SetUserVerification(UserVerificationRequirement::kRequired);
-    } else {
-      request.SetUserVerification(UserVerificationRequirement::kDiscouraged);
-    }
-  }
-
   task_ = std::make_unique<GetAssertionTask>(device_.get(), std::move(request),
                                              std::move(callback));
 }
@@ -247,6 +209,34 @@
   operation_->Start();
 }
 
+AuthenticatorSupportedOptions::ClientPinAvailability
+FidoDeviceAuthenticator::WillNeedPINToMakeCredential(const
+    CtapMakeCredentialRequest& request) {
+  if (request.user_verification() != UserVerificationRequirement::kRequired ||
+      Options()->user_verification_availability ==
+          AuthenticatorSupportedOptions::UserVerificationAvailability::
+              kSupportedAndConfigured) {
+    return AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported;
+  }
+
+  return Options()->client_pin_availability;
+}
+
+bool FidoDeviceAuthenticator::WillNeedPINToGetAssertion(
+    const CtapGetAssertionRequest& request) {
+  if (request.user_verification() ==
+          UserVerificationRequirement::kDiscouraged ||
+      Options()->user_verification_availability ==
+          AuthenticatorSupportedOptions::UserVerificationAvailability::
+              kSupportedAndConfigured) {
+    return false;
+  }
+
+  return Options()->client_pin_availability ==
+         AuthenticatorSupportedOptions::ClientPinAvailability::
+             kSupportedAndPinSet;
+}
+
 void FidoDeviceAuthenticator::Reset(ResetCallback callback) {
   DCHECK(device_->SupportedProtocolIsInitialized())
       << "InitializeAuthenticator() must be called first.";
diff --git a/device/fido/fido_device_authenticator.h b/device/fido/fido_device_authenticator.h
index fff40969..4005ee2 100644
--- a/device/fido/fido_device_authenticator.h
+++ b/device/fido/fido_device_authenticator.h
@@ -58,6 +58,13 @@
                  const std::string& new_pin,
                  pin::KeyAgreementResponse& peer_key,
                  SetPINCallback callback) override;
+  AuthenticatorSupportedOptions::ClientPinAvailability
+    WillNeedPINToMakeCredential(
+      const CtapMakeCredentialRequest& request) override;
+  // WillNeedPINToGetAssertion returns whether a PIN prompt will be needed to
+  // serve the given request on this authenticator.
+  bool WillNeedPINToGetAssertion(const CtapGetAssertionRequest& request)
+    override;
   void Reset(ResetCallback callback) override;
   void Cancel() override;
   std::string GetId() const override;
diff --git a/device/fido/get_assertion_handler_unittest.cc b/device/fido/get_assertion_handler_unittest.cc
index 2521ce24..b7c71770 100644
--- a/device/fido/get_assertion_handler_unittest.cc
+++ b/device/fido/get_assertion_handler_unittest.cc
@@ -40,7 +40,7 @@
 
 using TestGetAssertionRequestCallback = test::StatusAndValuesCallbackReceiver<
     FidoReturnCode,
-    base::Optional<AuthenticatorGetAssertionResponse>,
+    base::Optional<std::vector<AuthenticatorGetAssertionResponse>>,
     base::Optional<FidoTransportProtocol>>;
 
 }  // namespace
@@ -321,9 +321,10 @@
   EXPECT_TRUE(request_handler->is_complete());
   EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
   ASSERT_TRUE(response);
-  EXPECT_TRUE(response->credential());
+  ASSERT_EQ(1u, response->size());
+  EXPECT_TRUE(response.value()[0].credential());
   EXPECT_THAT(
-      response->raw_credential_id(),
+      response.value()[0].raw_credential_id(),
       ::testing::ElementsAreArray(test_data::kTestGetAssertionCredentialId));
 }
 
diff --git a/device/fido/get_assertion_request_handler.cc b/device/fido/get_assertion_request_handler.cc
index bc70bf2f..99575ba 100644
--- a/device/fido/get_assertion_request_handler.cc
+++ b/device/fido/get_assertion_request_handler.cc
@@ -8,6 +8,7 @@
 #include <set>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/feature_list.h"
@@ -197,23 +198,6 @@
 
 GetAssertionRequestHandler::~GetAssertionRequestHandler() = default;
 
-static bool WillNeedPIN(const CtapGetAssertionRequest& request,
-                        const FidoAuthenticator* authenticator) {
-  const auto& opt_options = authenticator->Options();
-  if (request.user_verification() ==
-          UserVerificationRequirement::kDiscouraged ||
-      !opt_options ||
-      opt_options->user_verification_availability ==
-          AuthenticatorSupportedOptions::UserVerificationAvailability::
-              kSupportedAndConfigured) {
-    return false;
-  }
-
-  return opt_options->client_pin_availability ==
-         AuthenticatorSupportedOptions::ClientPinAvailability::
-             kSupportedAndPinSet;
-}
-
 void GetAssertionRequestHandler::DispatchRequest(
     FidoAuthenticator* authenticator) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
@@ -223,18 +207,33 @@
     return;
   }
 
+  CtapGetAssertionRequest request(request_);
+
   if (base::FeatureList::IsEnabled(device::kWebAuthPINSupport) &&
-      WillNeedPIN(request_, authenticator)) {
-    CtapGetAssertionRequest request(request_);
+      authenticator->WillNeedPINToGetAssertion(request_)) {
+    request.SetUserVerification(UserVerificationRequirement::kRequired);
     // Set empty pinAuth to trigger just a touch.
     request.SetPinAuth({});
     authenticator->GetAssertion(
-        request, base::BindOnce(&GetAssertionRequestHandler::HandleResponse,
-                                weak_factory_.GetWeakPtr(), authenticator));
+        std::move(request),
+        base::BindOnce(&GetAssertionRequestHandler::HandleResponse,
+                       weak_factory_.GetWeakPtr(), authenticator));
   } else {
+    if (authenticator->Options()) {
+      if (authenticator->Options()->user_verification_availability ==
+              AuthenticatorSupportedOptions::UserVerificationAvailability::
+                  kSupportedAndConfigured &&
+          request_.user_verification() !=
+              UserVerificationRequirement::kDiscouraged) {
+        request.SetUserVerification(UserVerificationRequirement::kRequired);
+      } else {
+        request.SetUserVerification(UserVerificationRequirement::kDiscouraged);
+      }
+    }
     authenticator->GetAssertion(
-        request_, base::BindOnce(&GetAssertionRequestHandler::HandleResponse,
-                                 weak_factory_.GetWeakPtr(), authenticator));
+        std::move(request),
+        base::BindOnce(&GetAssertionRequestHandler::HandleResponse,
+                       weak_factory_.GetWeakPtr(), authenticator));
   }
 }
 
@@ -270,7 +269,7 @@
 
   if (state_ == State::kWaitingForTouch &&
       base::FeatureList::IsEnabled(device::kWebAuthPINSupport) &&
-      WillNeedPIN(request_, authenticator)) {
+      authenticator->WillNeedPINToGetAssertion(request_)) {
     if (response_code != CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid &&
         response_code != CtapDeviceResponseCode::kCtap2ErrPinInvalid) {
       VLOG(1) << "Expected invalid pinAuth error but got "
@@ -303,7 +302,9 @@
   }
 
   SetCredentialIdForResponseWithEmptyCredential(request_, *response);
-  OnAuthenticatorResponse(authenticator, response_code, std::move(response));
+  std::vector<AuthenticatorGetAssertionResponse> responses;
+  responses.emplace_back(std::move(*response));
+  OnAuthenticatorResponse(authenticator, response_code, std::move(responses));
 }
 
 void GetAssertionRequestHandler::OnRetriesResponse(
@@ -414,12 +415,15 @@
 
   observer()->FinishCollectPIN();
   state_ = State::kWaitingForSecondTouch;
-  request_.SetPinAuth(response->PinAuth(request_.client_data_hash()));
-  request_.SetPinProtocol(pin::kProtocolVersion);
+  CtapGetAssertionRequest request(request_);
+  request.SetPinAuth(response->PinAuth(request.client_data_hash()));
+  request.SetPinProtocol(pin::kProtocolVersion);
+  request.SetUserVerification(UserVerificationRequirement::kRequired);
 
   authenticator_->GetAssertion(
-      request_, base::BindOnce(&GetAssertionRequestHandler::HandleResponse,
-                               weak_factory_.GetWeakPtr(), authenticator_));
+      std::move(request),
+      base::BindOnce(&GetAssertionRequestHandler::HandleResponse,
+                     weak_factory_.GetWeakPtr(), authenticator_));
 }
 
 }  // namespace device
diff --git a/device/fido/get_assertion_request_handler.h b/device/fido/get_assertion_request_handler.h
index c52efca..6616f70 100644
--- a/device/fido/get_assertion_request_handler.h
+++ b/device/fido/get_assertion_request_handler.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/macros.h"
@@ -27,7 +28,8 @@
 class AuthenticatorGetAssertionResponse;
 
 class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionRequestHandler
-    : public FidoRequestHandler<AuthenticatorGetAssertionResponse> {
+    : public FidoRequestHandler<
+          std::vector<AuthenticatorGetAssertionResponse>> {
  public:
   GetAssertionRequestHandler(
       service_manager::Connector* connector,
diff --git a/device/fido/get_assertion_task.cc b/device/fido/get_assertion_task.cc
index 3ec9a9f..b1acf50 100644
--- a/device/fido/get_assertion_task.cc
+++ b/device/fido/get_assertion_task.cc
@@ -35,7 +35,12 @@
     : FidoTask(device),
       request_(std::move(request)),
       callback_(std::move(callback)),
-      weak_factory_(this) {}
+      weak_factory_(this) {
+  // The UV parameter should have been made binary by this point because CTAP2
+  // only takes a binary value.
+  DCHECK_NE(request_.user_verification(),
+            UserVerificationRequirement::kPreferred);
+}
 
 GetAssertionTask::~GetAssertionTask() = default;
 
diff --git a/device/fido/make_credential_request_handler.cc b/device/fido/make_credential_request_handler.cc
index fa54a9f..9070e09 100644
--- a/device/fido/make_credential_request_handler.cc
+++ b/device/fido/make_credential_request_handler.cc
@@ -115,40 +115,6 @@
   transport_availability_info().rp_id = request_parameter_.rp().rp_id();
   transport_availability_info().request_type =
       FidoRequestHandlerBase::RequestType::kMakeCredential;
-  Start();
-}
-
-MakeCredentialRequestHandler::~MakeCredentialRequestHandler() = default;
-
-// WillNeedPIN returns what type of PIN intervention will be needed to serve the
-// given request on the given authenticator.
-//   |kNotSupported|: no PIN involved.
-//   |kSupportedButPinNotSet|: will need to set a new PIN.
-//   |kSupportedAndPinSet|: will need to prompt for an existing PIN.
-static ClientPinAvailability WillNeedPIN(
-    const CtapMakeCredentialRequest& request,
-    const FidoAuthenticator* authenticator) {
-  const auto& opt_options = authenticator->Options();
-  if (request.user_verification() != UserVerificationRequirement::kRequired ||
-      !opt_options ||
-      opt_options->user_verification_availability ==
-          AuthenticatorSupportedOptions::UserVerificationAvailability::
-              kSupportedAndConfigured) {
-    return ClientPinAvailability::kNotSupported;
-  }
-
-  return opt_options->client_pin_availability;
-}
-
-void MakeCredentialRequestHandler::DispatchRequest(
-    FidoAuthenticator* authenticator) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
-
-  if (state_ != State::kWaitingForTouch ||
-      !CheckIfAuthenticatorSelectionCriteriaAreSatisfied(
-          authenticator, authenticator_selection_criteria_, observer())) {
-    return;
-  }
 
   // Set the rk, uv and attachment fields, which were only initialized to
   // default values up to here.  TODO(martinkr): Initialize these fields earlier
@@ -161,19 +127,46 @@
   request_parameter_.SetAuthenticatorAttachment(
       authenticator_selection_criteria_.authenticator_attachment());
 
+  Start();
+}
+
+MakeCredentialRequestHandler::~MakeCredentialRequestHandler() = default;
+
+void MakeCredentialRequestHandler::DispatchRequest(
+    FidoAuthenticator* authenticator) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
+
+  if (state_ != State::kWaitingForTouch ||
+      !CheckIfAuthenticatorSelectionCriteriaAreSatisfied(
+          authenticator, authenticator_selection_criteria_, observer())) {
+    return;
+  }
+
+  CtapMakeCredentialRequest request(request_parameter_);
+
   if (base::FeatureList::IsEnabled(device::kWebAuthPINSupport) &&
-      WillNeedPIN(request_parameter_, authenticator) !=
+      authenticator->WillNeedPINToMakeCredential(request_parameter_) !=
           ClientPinAvailability::kNotSupported) {
+    request.SetUserVerification(UserVerificationRequirement::kRequired);
     // Set an empty pinAuth parameter to wait for a touch and then report an
     // error.
-    CtapMakeCredentialRequest request(request_parameter_);
     request.SetPinAuth({});
     authenticator->MakeCredential(
-        request, base::BindOnce(&MakeCredentialRequestHandler::HandleResponse,
-                                weak_factory_.GetWeakPtr(), authenticator));
+        std::move(request),
+        base::BindOnce(&MakeCredentialRequestHandler::HandleResponse,
+                       weak_factory_.GetWeakPtr(), authenticator));
   } else {
+    if (authenticator->Options()) {
+      if (authenticator->Options()->user_verification_availability ==
+          AuthenticatorSupportedOptions::UserVerificationAvailability::
+              kSupportedAndConfigured) {
+        request.SetUserVerification(UserVerificationRequirement::kRequired);
+      } else {
+        request.SetUserVerification(UserVerificationRequirement::kDiscouraged);
+      }
+    }
     authenticator->MakeCredential(
-        request_parameter_,
+        std::move(request),
         base::BindOnce(&MakeCredentialRequestHandler::HandleResponse,
                        weak_factory_.GetWeakPtr(), authenticator));
   }
@@ -211,7 +204,7 @@
 
   if (base::FeatureList::IsEnabled(device::kWebAuthPINSupport) &&
       state_ == State::kWaitingForTouch) {
-    switch (WillNeedPIN(request_parameter_, authenticator)) {
+    switch (authenticator->WillNeedPINToMakeCredential(request_parameter_)) {
       case ClientPinAvailability::kSupportedAndPinSet:
         // Will need to get PIN to handle this request.
         if (response_code != CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid &&
@@ -411,12 +404,13 @@
 
   observer()->FinishCollectPIN();
   state_ = State::kWaitingForSecondTouch;
-  request_parameter_.SetPinAuth(
-      response->PinAuth(request_parameter_.client_data_hash()));
-  request_parameter_.SetPinProtocol(pin::kProtocolVersion);
+  CtapMakeCredentialRequest request(request_parameter_);
+  request.SetPinAuth(response->PinAuth(request.client_data_hash()));
+  request.SetPinProtocol(pin::kProtocolVersion);
+  request.SetUserVerification(UserVerificationRequirement::kRequired);
 
   authenticator_->MakeCredential(
-      request_parameter_,
+      std::move(request),
       base::BindOnce(&MakeCredentialRequestHandler::HandleResponse,
                      weak_factory_.GetWeakPtr(), authenticator_));
 }
diff --git a/device/fido/make_credential_task.cc b/device/fido/make_credential_task.cc
index 218049b9..184e725 100644
--- a/device/fido/make_credential_task.cc
+++ b/device/fido/make_credential_task.cc
@@ -49,7 +49,12 @@
     : FidoTask(device),
       request_parameter_(std::move(request_parameter)),
       callback_(std::move(callback)),
-      weak_factory_(this) {}
+      weak_factory_(this) {
+  // The UV parameter should have been made binary by this point because CTAP2
+  // only takes a binary value.
+  DCHECK_NE(request_parameter_.user_verification(),
+            UserVerificationRequirement::kPreferred);
+}
 
 MakeCredentialTask::~MakeCredentialTask() = default;
 
diff --git a/device/vr/android/gvr/gvr_device.cc b/device/vr/android/gvr/gvr_device.cc
index daf2c813..8c1b2a4b 100644
--- a/device/vr/android/gvr/gvr_device.cc
+++ b/device/vr/android/gvr/gvr_device.cc
@@ -227,18 +227,6 @@
   exclusive_controller_binding_.Close();
 }
 
-void GvrDevice::OnGetInlineFrameData(
-    mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
-  if (!gvr_api_) {
-    std::move(callback).Run(nullptr);
-    return;
-  }
-  mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
-  frame_data->pose =
-      GvrDelegate::GetVRPosePtrWithNeckModel(gvr_api_.get(), nullptr);
-  std::move(callback).Run(std::move(frame_data));
-}
-
 void GvrDevice::OnListeningForActivate(bool listening) {
   GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider();
   if (!delegate_provider)
@@ -365,13 +353,7 @@
     return;
   }
 
-  if (!options->immersive) {
-    // TODO(https://crbug.com/695937): This should be NOTREACHED() once we no
-    // longer need the hacked GVR non-immersive mode.  This should now only be
-    // hit if orientation devices are disabled by flag.
-    ReturnNonImmersiveSession(std::move(pending_request_session_callback_));
-    return;
-  }
+  DCHECK(options->immersive);
 
   // StartWebXRPresentation is async as we may trigger a DON (Device ON) flow
   // that pauses Chrome.
diff --git a/device/vr/android/gvr/gvr_device.h b/device/vr/android/gvr/gvr_device.h
index da05cef..875a272 100644
--- a/device/vr/android/gvr/gvr_device.h
+++ b/device/vr/android/gvr/gvr_device.h
@@ -45,8 +45,6 @@
  private:
   // VRDeviceBase
   void OnListeningForActivate(bool listening) override;
-  void OnGetInlineFrameData(
-      mojom::XRFrameDataProvider::GetFrameDataCallback callback) override;
 
   void OnStartPresentResult(mojom::XRSessionPtr session);
 
diff --git a/device/vr/oculus/oculus_device.cc b/device/vr/oculus/oculus_device.cc
index 8a37118..495af89 100644
--- a/device/vr/oculus/oculus_device.cc
+++ b/device/vr/oculus/oculus_device.cc
@@ -133,10 +133,7 @@
     return;
   }
 
-  if (!options->immersive) {
-    ReturnNonImmersiveSession(std::move(callback));
-    return;
-  }
+  DCHECK(options->immersive);
 
   StopOvrSession();
 
@@ -285,20 +282,6 @@
   }
 }
 
-void OculusDevice::OnGetInlineFrameData(
-    mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
-  if (!session_) {
-    std::move(callback).Run(nullptr);
-    return;
-  }
-
-  ovrTrackingState state = ovr_GetTrackingState(session_, 0, false);
-
-  mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
-  frame_data->pose = mojo::ConvertTo<mojom::VRPosePtr>(state.HeadPose.ThePose);
-  std::move(callback).Run(std::move(frame_data));
-}
-
 void OculusDevice::GetIsolatedXRGamepadProvider(
     mojom::IsolatedXRGamepadProviderRequest provider_request) {
   // We bind the provider_request on the render loop thread, so gamepad data is
diff --git a/device/vr/oculus/oculus_device.h b/device/vr/oculus/oculus_device.h
index a825d2d1..5134c3bf 100644
--- a/device/vr/oculus/oculus_device.h
+++ b/device/vr/oculus/oculus_device.h
@@ -37,8 +37,6 @@
   void EnsureInitialized(int render_process_id,
                          int render_frame_id,
                          EnsureInitializedCallback callback) override;
-  void OnGetInlineFrameData(
-      mojom::XRFrameDataProvider::GetFrameDataCallback callback) override;
   void OnRequestSessionResult(mojom::XRRuntime::RequestSessionCallback callback,
                               bool result,
                               mojom::XRSessionPtr session);
diff --git a/device/vr/openvr/openvr_device.cc b/device/vr/openvr/openvr_device.cc
index 7d2bdb8..a1b02aee 100644
--- a/device/vr/openvr/openvr_device.cc
+++ b/device/vr/openvr/openvr_device.cc
@@ -181,10 +181,7 @@
     return;
   }
 
-  if (!options->immersive) {
-    ReturnNonImmersiveSession(std::move(callback));
-    return;
-  }
+  DCHECK(options->immersive);
 
   if (!render_loop_->IsRunning()) {
     render_loop_->Start();
@@ -334,23 +331,6 @@
   exclusive_controller_binding_.Close();
 }
 
-void OpenVRDevice::OnGetInlineFrameData(
-    mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
-  if (!openvr_) {
-    std::move(callback).Run(nullptr);
-    return;
-  }
-  const float kPredictionTimeSeconds = 0.03f;
-  vr::TrackedDevicePose_t rendering_poses[vr::k_unMaxTrackedDeviceCount];
-  openvr_->GetSystem()->GetDeviceToAbsoluteTrackingPose(
-      vr::TrackingUniverseSeated, kPredictionTimeSeconds, rendering_poses,
-      vr::k_unMaxTrackedDeviceCount);
-  mojom::XRFrameDataPtr data = mojom::XRFrameData::New();
-  data->pose = mojo::ConvertTo<mojom::VRPosePtr>(
-      rendering_poses[vr::k_unTrackedDeviceIndex_Hmd]);
-  std::move(callback).Run(std::move(data));
-}
-
 // Only deal with events that will cause displayInfo changes for now.
 void OpenVRDevice::OnPollingEvents() {
   main_thread_task_runner_->PostDelayedTask(
diff --git a/device/vr/openvr/openvr_device.h b/device/vr/openvr/openvr_device.h
index cbec7eb..fed1fca8 100644
--- a/device/vr/openvr/openvr_device.h
+++ b/device/vr/openvr/openvr_device.h
@@ -52,10 +52,6 @@
   mojom::XRCompositorHostPtr BindCompositorHost();
 
  private:
-  // VRDeviceBase
-  void OnGetInlineFrameData(
-      mojom::XRFrameDataProvider::GetFrameDataCallback callback) override;
-
   // XRSessionController
   void SetFrameDataRestricted(bool restricted) override;
 
diff --git a/device/vr/windows_mixed_reality/mixed_reality_device.cc b/device/vr/windows_mixed_reality/mixed_reality_device.cc
index 7f8c7d0..cd792a7 100644
--- a/device/vr/windows_mixed_reality/mixed_reality_device.cc
+++ b/device/vr/windows_mixed_reality/mixed_reality_device.cc
@@ -88,10 +88,7 @@
 void MixedRealityDevice::RequestSession(
     mojom::XRRuntimeSessionOptionsPtr options,
     mojom::XRRuntime::RequestSessionCallback callback) {
-  if (!options->immersive) {
-    ReturnNonImmersiveSession(std::move(callback));
-    return;
-  }
+  DCHECK(options->immersive);
 
   if (!render_loop_)
     CreateRenderLoop();
diff --git a/docs/gpu/gpu_testing_bot_details.md b/docs/gpu/gpu_testing_bot_details.md
index 6a4d76ea..3936bf4 100644
--- a/docs/gpu/gpu_testing_bot_details.md
+++ b/docs/gpu/gpu_testing_bot_details.md
@@ -227,6 +227,7 @@
 
 [infradata/config]:                https://chrome-internal.googlesource.com/infradata/config
 [configs/chromium-swarm/bots.cfg]: https://chrome-internal.googlesource.com/infradata/config/+/master/configs/chromium-swarm/bots.cfg
+[bot_config.py]:                   https://chrome-internal.googlesource.com/infradata/config/+/master/configs/chromium-swarm/scripts/bot_config.py
 
 ## Walkthroughs of various maintenance scenarios
 
@@ -561,14 +562,16 @@
 ### How to test and deploy a driver update
 
 Let's say that you want to roll out an update to the graphics drivers on one of
-the configurations like the Win7 NVIDIA bots. The responsible way to do this is
-to run the new driver on one of the waterfalls for a day or two to make sure
-the tests are running reliably green before rolling out the driver update
-everywhere. To do this:
+the configurations like the Win10 NVIDIA bots. In order to verify that the new
+driver won't destabilize Chromium's commit queue, it's necessary to run the new
+driver on one of the waterfalls for a day or two to make sure the tests are
+reliably green before rolling out the driver update. To do this:
 
 1.  Make sure that all of the current Swarming jobs for this OS and GPU
     configuration are targeted at the "stable" version of the driver in
-    [waterfalls.pyl].
+    [waterfalls.pyl] and [mixins.pyl]. Make sure that there is a "named" stable
+    version of the driver there, which targets the _TARGETED_DRIVER_VERSIONS
+    dictionary in [bot_config.py] (Google internal).
 1.  File a `Build Infrastructure` bug, component `Infra>Labs`, to have ~4 of the
     physical machines already in the Swarming pool upgraded to the new version
     of the driver.
@@ -577,53 +580,33 @@
     waterfall](#How-to-add-a-new-tester-bot-to-the-chromium_gpu_fyi-waterfall)
     to deploy one.
 1.  Have this experimental bot target the new version of the driver in
-    [waterfalls.pyl].
+    [waterfalls.pyl] and [mixins.pyl].
 1.  Hopefully, the new machine will pass the pixel tests. If it doesn't, then
     unfortunately, it'll be necessary to follow the instructions on
     [updating the pixel tests] to temporarily suppress the failures on this
     particular configuration. Keep the time window for these test suppressions
     as narrow as possible.
 1.  Watch the new machine for a day or two to make sure it's stable.
-1.  When it is, update [mixins.pyl] to add a mixin to *optionally* use
-    the new driver version. The syntax looks like this:
+1.  When it is, update [bot_config.py] (Google internal) to *add* a mapping
+    between the new driver version and the "stable" version. For example:
 <pre>
-  'win10_nvidia_quadro_p400_upgrade': {
-    'swarming': {
-      'optional_dimensions': {
-        # Wait 10 minutes for this new driver version and then fall back to the
-        # current "stable" driver version. The format for optional dimensions
-        # is: expiration: [{key, value}, ..].
-        600: [
-          {
-            'gpu': '10de:1cb3-24.21.14.1195',
-          }
-        ],
-      },
-    }
-  },
+  _TARGETED_DRIVER_VERSIONS = {
+    # NVIDIA Quadro P400, Ubuntu Stable version
+    '10de:1cb3-384.90': 'nvidia-quadro-p400-ubuntu-stable',
+    # NVIDIA Quadro P400, new Ubuntu Stable version
+    '10de:1cb3-410.78': 'nvidia-quadro-p400-ubuntu-stable',
+    # ...
+  }
 </pre>
-
     The new driver version should match the one just added for the
-    experimental bot. A separate mixin must be used because the syntax
-    is different from these optional, or fallback, dimensions. See
-    [https://chromium-review.googlesource.com/1376653](https://chromium-review.googlesource.com/1376653)
-    for an example of how this was used to perform a recent OS
-    upgrade. [This
-    CL](https://chromium-review.googlesource.com/1396604) shows an
-    example of an actual driver upgrade, but using older "trigger
-    script" functionality no longer recommended for this purpose.
-
-1.  In the same CL, modify [waterfalls.pyl], adding that mixin to all
-    of the bots being upgraded. Note that it must just be *added*; it
-    does not *replace* the bot's current "stable" graphics driver mixin.
-1.  After that lands, ask the Chrome Infrastructure Labs team to roll out the
+    experimental bot. Get this CL reviewed and landed.
+1.  After it lands, ask the Chrome Infrastructure Labs team to roll out the
     driver update across all of the similarly configured bots in the swarming
     pool.
 1.  If necessary, update pixel test expectations and remove the suppressions
     added above.
-1.  Remove the upgrade mixin from [mixins.pyl] and the references from
-    [waterfalls.pyl], and change the bot's stable dimensions to the upgraded
-    ones.
+1.  Remove the old driver version from [bot_config.pyl], leaving the "stable"
+    driver version pointing at the newly upgraded version.
 
 Note that we leave the experimental bot in place. We could reclaim it, but it
 seems worthwhile to continuously test the "next" version of graphics drivers as
diff --git a/docs/gpu/pixel_wrangling.md b/docs/gpu/pixel_wrangling.md
index 68630aed..dac7319f 100644
--- a/docs/gpu/pixel_wrangling.md
+++ b/docs/gpu/pixel_wrangling.md
@@ -14,24 +14,14 @@
 
 ## Fleet Status
 
-The following links (sorry, Google employees only) show the status of various
-GPU bots in the fleet.
+*   [Chrome GPU Fleet Status](http://vi/chrome-infra/Projects/gpu)
 
-Primary configurations:
+(Sorry, this link is Google internal only.)
 
-*   [Windows 10 Quadro P400 Pool](http://shortn/_dmtaFfY2Jq)
-*   [Windows 10 Intel HD 630 Pool](http://shortn/_QsoGIGIFYd)
-*   [Linux Quadro P400 Pool](http://shortn/_fNgNs1uROQ)
-*   [Linux Intel HD 630 Pool](http://shortn/_dqEGjCGMHT)
-*   [Mac AMD Retina 10.13.6 GPU Pool](http://shortn/_m26tivRkUp)
-*   [Mac Mini Chrome Pool](http://shortn/_Ru8NESapPM)
-*   [Android Nexus 5X Chrome Pool](http://shortn/_G3j7AVmuNR)
+These graphs show 1 day of activity by default. The drop-down boxes at the top
+allow viewing of longer durations.
 
-Secondary configurations:
-
-*   [Windows 7 Quadro P400 Pool](http://shortn/_cuxSKC15UX)
-*   [Windows AMD R7 240 GPU Pool](http://shortn/_XET7RTMHQm)
-*   [Mac NVIDIA Retina 10.13.6 GPU Pool](http://shortn/_ooNMNbCleT)
+See [this CL](http://cl/238562533) for an example of how to update these graphs.
 
 ## GPU Bots' Waterfalls
 
diff --git a/extensions/browser/url_loader_factory_manager.cc b/extensions/browser/url_loader_factory_manager.cc
index 0beb4e7..911a2f4 100644
--- a/extensions/browser/url_loader_factory_manager.cc
+++ b/extensions/browser/url_loader_factory_manager.cc
@@ -65,12 +65,15 @@
     "072D729E856B1F2C9894AEEC3A5DF65E519D6BEE",
     "07333481B7B8D7F57A9BA64FB98CF86EA87455FC",
     "086E69ED9071DCB20C93A081A68360963AB09385",
+    "0C011D916B15E5451E1B84BD14397B8EC98F455B",
     "0CB16BAEE070B7617E9188B387C44964FB705D79",
     "0EAEA2FDEE025D95B3ABB37014EFF5A98AC4BEAE",
+    "0FCD1282065485458E630683F098F591B24C406D",
     "109A37B889E7C8AEA7B0103559C3EB6AF73B7832",
     "16A81AEA09A67B03F7AEA5B957D24A4095E764BE",
     "177508B365CBF1610CC2B53707749D79272F2F0B",
     "1AB9CC404876117F49135E67BAD813F935AAE9BA",
+    "1B9251EF3EDD5A2B2872B168406F36FB18C72F37",
     "1CDDF7436E5F891E1D5E37164F7EB992AECA0E2D",
     "1DB115A4344B58C0B7CC1595662E86C9E92C0848",
     "1E37F1A19C1C528E616637B105CFC4838ECF52B4",
@@ -91,6 +94,7 @@
     "41536C3554CD9458EB2F05F1B58CF84BB7BF83BC",
     "43865F8D555C201FE452A5A40702EA96240E749C",
     "44943FADD66932EF56EE3D856A9FAAD4A8AF0FD9",
+    "47ADBB376050C083FFC54CC28CD3D1F54BF0BFED",
     "4913450195D177430217CAB64B356DC6F6B0F483",
     "5053323D1F7B6EEC97A77A350DB6D0D8E51CD0AC",
     "505F2C1E723731B2C8C9182FEAA73D00525B2048",
@@ -109,6 +113,7 @@
     "6E49449D56D031B87835CC767734AF5A064E1A13",
     "71CB78C3334D5122E7F23C8525AD24100CDE7D4A",
     "71EE66C0F71CD89BEE340F8568A44101D4C3A9A7",
+    "7527942941BFF13D66B46E7A2A56FDBA873FB9E6",
     "77D83E0A4157A0E77B51AD60BAB69A346CD4FEA3",
     "7879DB88205D880B64D55E51B9726E1D12F7261F",
     "7BFE588B209A15260DE12777B4BBB738DE98FE6C",
@@ -127,6 +132,8 @@
     "97E04C5632954E778306CAC40B3F95C470B463B6",
     "98EF7B1601119AEE1FCC28EE5CE247DED5676539",
     "99E06C364BBB2D1F82A9D20BC1645BF21E478259",
+    "A04F08A772F1C83B7A14ED29788ACA4F000BBE05",
+    "A07DA0EDB967D027E3B220208AD085FDC44C3231",
     "A30E526CF62131BFBFD7CD9B56253A8F3F171777",
     "A3660FA31A0DBF07C9F80D5342FF215DBC962719",
     "A6057397EDC4E6CF25BC3A142F866ACC653B1CF1",
@@ -147,8 +154,10 @@
     "CD8AF9C47DDE6327F8D9A3EFA81F34C6B6C26EBB",
     "CF40F6289951CBFA3B83B792EFA774E2EA06E4C0",
     "D0537B1BADCE856227CE76E31B3772F6B68F653C",
+    "D347F78F32567E90BC32D9C16B085254EA269590",
     "D9A97CD75380C697C65D37512E53DBECDFA45FB9",
     "DDA21167F058A65D878DF84C3CF3FCC60B053E80",
+    "E134BC4A0FF6C59CE42CC76BA6B2D6F5DC648EC4",
     "E178D4F4D6617C0B880C36F192DA3B18422C5064",
     "E6B12430B6166B31BE20E13941C22569EA75B0F2",
     "E7036E906DBFB77C46EDDEB003A72C0B5CC9BE7F",
@@ -157,6 +166,7 @@
     "EC24668224116D19FF1A5FFAA61238B88773982C",
     "EC4A841BD03C8E5202043165188A9E060BF703A3",
     "EE4BE5F23D2E59E4713958465941EFB4A18166B7",
+    "F1ACA279F460440E47078D91FE372212DD9B8709",
     "F273C23C616F5C56E8EDBAE24B21F5D408936A0D",
     "F566B33D62CE21415AF5B3F3FD8762B7454B8874",
     "F59AB261280AB3AE9826D9359507838B90B07431",
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index 096ffaf..f145be91 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -257,7 +257,6 @@
       "command_buffer/tests/webgpu_fence_unittest.cc",
       "command_buffer/tests/webgpu_test.cc",
       "command_buffer/tests/webgpu_test.h",
-      "ipc/client/webgpu_in_process_context_tests.cc",
     ]
   }
 
diff --git a/gpu/command_buffer/common/gpu_memory_buffer_support.cc b/gpu/command_buffer/common/gpu_memory_buffer_support.cc
index bac710c7..1dd2072 100644
--- a/gpu/command_buffer/common/gpu_memory_buffer_support.cc
+++ b/gpu/command_buffer/common/gpu_memory_buffer_support.cc
@@ -117,6 +117,11 @@
       format == gfx::BufferFormat::BGRX_8888) {
     return false;
   }
+#elif defined(OS_ANDROID)
+  if (format == gfx::BufferFormat::BGR_565 ||
+      format == gfx::BufferFormat::RGBA_8888) {
+    return false;
+  }
 #endif
   return true;
 }
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 21f408fb..bdf6b80 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -95,21 +95,6 @@
 
 base::AtomicSequenceNumber g_raster_decoder_id;
 
-class ScopedProgressReporter {
- public:
-  ScopedProgressReporter(gl::ProgressReporter* reporter) : reporter_(reporter) {
-    if (reporter_)
-      reporter_->ReportProgress();
-  }
-  ~ScopedProgressReporter() {
-    if (reporter_)
-      reporter_->ReportProgress();
-  }
-
- private:
-  gl::ProgressReporter* reporter_;
-};
-
 // This class prevents any GL errors that occur when it is in scope from
 // being reported to the client.
 class ScopedGLErrorSuppressor {
@@ -2149,7 +2134,7 @@
   options.crash_dump_on_failure = true;
 
   size_t paint_buffer_size = raster_shm_size;
-  ScopedProgressReporter report_progress(
+  gl::ScopedProgressReporter report_progress(
       shared_context_state_->progress_reporter());
   while (paint_buffer_size > 0) {
     size_t skip = 0;
@@ -2192,7 +2177,7 @@
     // This is a slow operation since skia will execute the GPU work for the
     // complete tile. Make sure the progress reporter is notified to avoid
     // hangs.
-    ScopedProgressReporter report_progress(
+    gl::ScopedProgressReporter report_progress(
         shared_context_state_->progress_reporter());
     sk_surface_->prepareForExternalIO();
   }
diff --git a/gpu/command_buffer/tests/webgpu_fence_unittest.cc b/gpu/command_buffer/tests/webgpu_fence_unittest.cc
index 58a41510..bc2e96f 100644
--- a/gpu/command_buffer/tests/webgpu_fence_unittest.cc
+++ b/gpu/command_buffer/tests/webgpu_fence_unittest.cc
@@ -53,6 +53,9 @@
 
 // Test that getting the value of the fence is the initial value.
 TEST_F(WebGPUFenceTest, InitialValue) {
+  if (!WebGPUSupported()) {
+    GTEST_SKIP();
+  }
   dawn::Device device = dawn::Device::Acquire(webgpu()->GetDefaultDevice());
   dawn::Queue queue = device.CreateQueue();
   {
@@ -69,6 +72,9 @@
 
 // Test that after signaling a fence, its completed value gets updated.
 TEST_F(WebGPUFenceTest, GetCompletedValue) {
+  if (!WebGPUSupported()) {
+    GTEST_SKIP();
+  }
   dawn::Device device = dawn::Device::Acquire(webgpu()->GetDefaultDevice());
   dawn::Queue queue = device.CreateQueue();
   dawn::FenceDescriptor fence_desc{nullptr, 0};
@@ -81,6 +87,9 @@
 // Test that a fence's OnCompletion handler is called after the signal value
 // is completed.
 TEST_F(WebGPUFenceTest, OnCompletion) {
+  if (!WebGPUSupported()) {
+    GTEST_SKIP();
+  }
   dawn::Device device = dawn::Device::Acquire(webgpu()->GetDefaultDevice());
   dawn::Queue queue = device.CreateQueue();
   dawn::FenceDescriptor fence_desc{nullptr, 0};
@@ -97,6 +106,9 @@
 
 // Test signaling a fence a million times.
 TEST_F(WebGPUFenceTest, SignalManyTimes) {
+  if (!WebGPUSupported()) {
+    GTEST_SKIP();
+  }
   dawn::Device device = dawn::Device::Acquire(webgpu()->GetDefaultDevice());
   dawn::Queue queue = device.CreateQueue();
   dawn::FenceDescriptor fence_desc{nullptr, 0};
diff --git a/gpu/command_buffer/tests/webgpu_test.cc b/gpu/command_buffer/tests/webgpu_test.cc
index d6d8a18..fb51ce6 100644
--- a/gpu/command_buffer/tests/webgpu_test.cc
+++ b/gpu/command_buffer/tests/webgpu_test.cc
@@ -9,6 +9,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "gpu/command_buffer/client/webgpu_implementation.h"
 #include "gpu/command_buffer/service/webgpu_decoder.h"
+#include "gpu/config/gpu_test_config.h"
 #include "gpu/ipc/in_process_command_buffer.h"
 #include "gpu/ipc/in_process_gpu_thread_holder.h"
 #include "gpu/ipc/webgpu_in_process_context.h"
@@ -21,6 +22,11 @@
 WebGPUTest::WebGPUTest() = default;
 WebGPUTest::~WebGPUTest() = default;
 
+bool WebGPUTest::WebGPUSupported() {
+  // crbug.com(941685): Vulkan driver crashes on Linux FYI Release (AMD R7 240).
+  return !GPUTestBotConfig::CurrentConfigMatches("Linux AMD");
+}
+
 void WebGPUTest::SetUp() {
   gpu_thread_holder_ = std::make_unique<InProcessGpuThreadHolder>();
   gpu_thread_holder_->GetGpuPreferences()->enable_webgpu = true;
@@ -31,6 +37,9 @@
 }
 
 void WebGPUTest::Initialize(const Options& options) {
+  if (!WebGPUSupported()) {
+    return;
+  }
   ContextCreationAttribs attributes;
   attributes.bind_generates_resource = false;
   attributes.enable_gles2_interface = false;
@@ -58,4 +67,12 @@
   context_->GetTaskRunner()->RunPendingTasks();
 }
 
+TEST_F(WebGPUTest, Dummy) {
+  if (!WebGPUSupported()) {
+    GTEST_SKIP();
+  }
+  Initialize(WebGPUTest::Options());
+  webgpu()->Dummy();
+}
+
 }  // namespace gpu
diff --git a/gpu/command_buffer/tests/webgpu_test.h b/gpu/command_buffer/tests/webgpu_test.h
index 205f29e..c51cb5f 100644
--- a/gpu/command_buffer/tests/webgpu_test.h
+++ b/gpu/command_buffer/tests/webgpu_test.h
@@ -33,6 +33,7 @@
   WebGPUTest();
   ~WebGPUTest() override;
 
+  bool WebGPUSupported();
   void SetUp() override;
   void TearDown() override;
 
diff --git a/gpu/config/gpu_crash_keys.cc b/gpu/config/gpu_crash_keys.cc
index 9218c7a..b4f99e66f 100644
--- a/gpu/config/gpu_crash_keys.cc
+++ b/gpu/config/gpu_crash_keys.cc
@@ -22,6 +22,10 @@
 #endif
 crash_reporter::CrashKeyString<4> gpu_gl_context_is_virtual(
     "gpu-gl-context-is-virtual");
+crash_reporter::CrashKeyString<20> seconds_since_last_progress_report(
+    "seconds-since-last-progress-report");
+crash_reporter::CrashKeyString<20> available_physical_memory_in_mb(
+    "available-physical-memory-in-mb");
 
 }  // namespace crash_keys
 }  // namespace gpu
diff --git a/gpu/config/gpu_crash_keys.h b/gpu/config/gpu_crash_keys.h
index 3d099b7..1f1b7e7 100644
--- a/gpu/config/gpu_crash_keys.h
+++ b/gpu/config/gpu_crash_keys.h
@@ -27,6 +27,10 @@
 extern GPU_EXPORT crash_reporter::CrashKeyString<128> gpu_renderer;
 #endif
 extern GPU_EXPORT crash_reporter::CrashKeyString<4> gpu_gl_context_is_virtual;
+extern GPU_EXPORT crash_reporter::CrashKeyString<20>
+    seconds_since_last_progress_report;
+extern GPU_EXPORT crash_reporter::CrashKeyString<20>
+    available_physical_memory_in_mb;
 
 }  // namespace crash_keys
 }  // namespace gpu
diff --git a/gpu/ipc/client/webgpu_in_process_context_tests.cc b/gpu/ipc/client/webgpu_in_process_context_tests.cc
deleted file mode 100644
index a09dd4e..0000000
--- a/gpu/ipc/client/webgpu_in_process_context_tests.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-
-#include "base/command_line.h"
-#include "build/build_config.h"
-#include "gpu/command_buffer/client/shared_memory_limits.h"
-#include "gpu/command_buffer/client/webgpu_implementation.h"
-#include "gpu/config/gpu_switches.h"
-#include "gpu/ipc/in_process_gpu_thread_holder.h"
-#include "gpu/ipc/webgpu_in_process_context.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace gpu {
-
-namespace {
-
-class WebGPUInProcessCommandBufferTest : public ::testing::Test {
- public:
-  WebGPUInProcessCommandBufferTest() {
-    gpu_thread_holder_.GetGpuPreferences()->enable_webgpu = true;
-  }
-
-  std::unique_ptr<WebGPUInProcessContext> CreateWebGPUInProcessContext() {
-    ContextCreationAttribs attributes;
-    attributes.bind_generates_resource = false;
-    attributes.enable_gles2_interface = false;
-    attributes.context_type = CONTEXT_TYPE_WEBGPU;
-
-    static constexpr GpuMemoryBufferManager* memory_buffer_manager = nullptr;
-    static constexpr ImageFactory* image_factory = nullptr;
-    static constexpr GpuChannelManagerDelegate* channel_manager = nullptr;
-    auto context = std::make_unique<WebGPUInProcessContext>();
-    auto result = context->Initialize(
-        gpu_thread_holder_.GetTaskExecutor(), attributes, SharedMemoryLimits(),
-        memory_buffer_manager, image_factory, channel_manager);
-    DCHECK_EQ(result, ContextResult::kSuccess);
-    return context;
-  }
-
-  void SetUp() override {
-    context_ = CreateWebGPUInProcessContext();
-    wg_ = context_->GetImplementation();
-  }
-
-  void TearDown() override { context_.reset(); }
-
- protected:
-  InProcessGpuThreadHolder gpu_thread_holder_;
-  webgpu::WebGPUInterface* wg_;  // not owned
-  std::unique_ptr<WebGPUInProcessContext> context_;
-};
-
-}  // namespace
-
-TEST_F(WebGPUInProcessCommandBufferTest, Dummy) {
-  wg_->Dummy();
-  // TODO
-}
-
-}  // namespace gpu
diff --git a/gpu/ipc/service/command_buffer_stub.cc b/gpu/ipc/service/command_buffer_stub.cc
index 1adfcb0f..de98b83 100644
--- a/gpu/ipc/service/command_buffer_stub.cc
+++ b/gpu/ipc/service/command_buffer_stub.cc
@@ -415,7 +415,7 @@
 CommandBufferStub::OnCommandBatchProcessed() {
   GpuWatchdogThread* watchdog = channel_->gpu_channel_manager()->watchdog();
   if (watchdog)
-    watchdog->CheckArmed();
+    watchdog->ReportProgress();
   bool pause = channel_->scheduler()->ShouldYield(sequence_id_);
   return pause ? kPauseExecution : kContinueExecution;
 }
diff --git a/gpu/ipc/service/gpu_watchdog_thread.cc b/gpu/ipc/service/gpu_watchdog_thread.cc
index 00e0b66..6521b52 100644
--- a/gpu/ipc/service/gpu_watchdog_thread.cc
+++ b/gpu/ipc/service/gpu_watchdog_thread.cc
@@ -21,10 +21,13 @@
 #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"
 
 #if defined(OS_WIN)
 #include <windows.h>
@@ -110,6 +113,7 @@
 }
 
 void GpuWatchdogThread::CheckArmed() {
+  last_reported_progress_timeticks_ = base::TimeTicks::Now();
   // If the watchdog is |awaiting_acknowledge_|, reset this variable to false
   // and post an acknowledge task now. No barrier is needed as
   // |awaiting_acknowledge_| is only ever read from this thread.
@@ -511,6 +515,16 @@
     handler(logging::LOG_ERROR, __FILE__, __LINE__, 0, message);
   DLOG(ERROR) << message;
 
+  int64_t since_last_progress_report =
+      (current_timeticks - last_reported_progress_timeticks_).InSeconds();
+  crash_keys::seconds_since_last_progress_report.Set(
+      base::Int64ToString(since_last_progress_report));
+
+  int64_t available_physical_memory =
+      base::SysInfo::AmountOfAvailablePhysicalMemory() >> 20;
+  crash_keys::available_physical_memory_in_mb.Set(
+      base::Int64ToString(available_physical_memory));
+
   // Deliberately crash the process to create a crash dump.
   *((volatile int*)0) = 0x1337;
 
diff --git a/gpu/ipc/service/gpu_watchdog_thread.h b/gpu/ipc/service/gpu_watchdog_thread.h
index 01cbfa0..6bb08e4 100644
--- a/gpu/ipc/service/gpu_watchdog_thread.h
+++ b/gpu/ipc/service/gpu_watchdog_thread.h
@@ -36,8 +36,6 @@
 
   static std::unique_ptr<GpuWatchdogThread> Create(bool start_backgrounded);
 
-  void CheckArmed();
-
   // Must be called after a PowerMonitor has been created. Can be called from
   // any thread.
   void AddPowerObserver();
@@ -113,6 +111,8 @@
 
   GpuWatchdogThread();
 
+  void CheckArmed();
+
   void OnAcknowledge();
   void OnCheck(bool after_suspend);
   void OnCheckTimeout();
@@ -187,6 +187,8 @@
   base::Time check_time_;
   base::TimeTicks check_timeticks_;
 
+  base::TimeTicks last_reported_progress_timeticks_;
+
 #if defined(USE_X11)
   XDisplay* display_;
   gfx::AcceleratedWidget window_;
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 87bf01d1..cc757d389 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -135,6 +135,7 @@
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h"
 #import "ios/chrome/browser/url_loading/app_url_loading_service.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_service_factory.h"
 #import "ios/chrome/browser/web/tab_id_tab_helper.h"
@@ -1583,9 +1584,10 @@
       [self
           dismissModalDialogsWithCompletion:^{
             [self setCurrentInterfaceForMode:mode];
-            UrlLoadingServiceFactory::GetForBrowserState(
-                [self.currentBVC browserState])
-                ->LoadUrlInNewTab(command);
+            // TODO(crbug.com/907527): Refactor to use Load().
+            [UrlLoadingServiceFactory::GetForBrowserState(
+                 [self.currentBVC browserState])
+                    ->GetUrlLoader() webPageOrderedOpen:command];
           }
                              dismissOmnibox:YES];
     }
@@ -1861,7 +1863,7 @@
   web::NavigationManager::WebLoadParams params(result);
   params.transition_type = ui::PAGE_TRANSITION_TYPED;
   UrlLoadingServiceFactory::GetForBrowserState([self.currentBVC browserState])
-      ->LoadUrlInCurrentTab(ChromeLoadParams(params));
+      ->Load(UrlLoadParams::InCurrentTab(params));
 }
 
 // Loads the image from startup parameters as search-by-image in the current
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h b/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h
index 8d6c0e0a..25a2b77 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h
+++ b/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h
@@ -28,6 +28,12 @@
   // was set, this returns an empty string.
   base::string16 GetDetailsMessageText() const;
 
+  // The Username being saved or updated by the Infobar.
+  NSString* GetUserNameText() const;
+
+  // The URL host for which the credentials are being saved for.
+  NSString* GetURLHostText() const;
+
  protected:
   IOSChromePasswordManagerInfoBarDelegate(
       bool is_sync_user,
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.mm b/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.mm
index 6632c29c..1071c24 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.mm
+++ b/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.mm
@@ -7,10 +7,12 @@
 #include <utility>
 
 #include "base/strings/string16.h"
+#include "base/strings/sys_string_conversions.h"
 #include "components/password_manager/core/browser/password_form_manager_for_ui.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -34,6 +36,15 @@
                        : base::string16();
 }
 
+NSString* IOSChromePasswordManagerInfoBarDelegate::GetUserNameText() const {
+  return base::SysUTF16ToNSString(
+      form_to_save_->GetPendingCredentials().username_value);
+}
+
+NSString* IOSChromePasswordManagerInfoBarDelegate::GetURLHostText() const {
+  return base::SysUTF8ToNSString(form_to_save_->GetOrigin().host());
+}
+
 int IOSChromePasswordManagerInfoBarDelegate::GetIconId() const {
   return IDR_IOS_INFOBAR_SAVE_PASSWORD;
 }
diff --git a/ios/chrome/browser/passwords/password_controller.h b/ios/chrome/browser/passwords/password_controller.h
index f6bbc86..389c6f0 100644
--- a/ios/chrome/browser/passwords/password_controller.h
+++ b/ios/chrome/browser/passwords/password_controller.h
@@ -79,15 +79,6 @@
 
 - (instancetype)init NS_UNAVAILABLE;
 
-// Generates and offers a password to the user based on given |formName| for the
-// given (optional) fields |newPasswordIdentfier| and
-// |confirmPasswordIdentfier|. |completionHandler| called with YES if user
-// accepted the generated password.
-- (void)generatePasswordForFormName:(NSString*)formName
-              newPasswordIdentifier:(NSString*)newPasswordIdentifier
-          confirmPasswordIdentifier:(NSString*)confirmPasswordIdentifier
-                  completionHandler:(void (^)(BOOL))completionHandler;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_PASSWORDS_PASSWORD_CONTROLLER_H_
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index d79b88f9..40988e9 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -364,11 +364,11 @@
 
   if (self.isPasswordGenerated &&
       [fieldIdentifier isEqualToString:self.passwordGeneratedIdentifier]) {
-    // TODO(crbug.com/886583):  On other platforms, when the user clicks on
-    // generation field, we show password in clear text. And the user has
-    // the possibility to edit it. On iOS, it's harder to do (it's probably
-    // bad idea to change field type from password to text).
-    // We probably need to show some additionaly UI.  Waiting on UX.
+    // On other platforms, when the user clicks on generation field, we show
+    // password in clear text. And the user has the possibility to edit it. On
+    // iOS, it's harder to do (it's probably bad idea to change field type from
+    // password to text). The decision was to give everything to the automatic
+    // flow and avoid the manual flow, for a cleaner and simpler UI.
     if (typedValue.length < kMinimumLengthForEditedPassword) {
       self.isPasswordGenerated = NO;
       self.passwordGeneratedIdentifier = nil;
@@ -718,24 +718,9 @@
 
 - (void)generatePasswordForFormName:(NSString*)formName
                   completionHandler:(void (^)(BOOL))completionHandler {
-  const NewPasswordFormGenerationData* generation_data =
-      [self getFormForGenerationFromFormName:formName];
-  if (!generation_data)
+  if (![self getFormForGenerationFromFormName:formName])
     return;
-  NSString* newPasswordIdentifier =
-      SysUTF16ToNSString(generation_data->new_password_element);
-  NSString* confirmPasswordIdentifier =
-      SysUTF16ToNSString(generation_data->confirmation_password_element);
-  [self generatePasswordForFormName:formName
-              newPasswordIdentifier:newPasswordIdentifier
-          confirmPasswordIdentifier:confirmPasswordIdentifier
-                  completionHandler:completionHandler];
-}
 
-- (void)generatePasswordForFormName:(NSString*)formName
-              newPasswordIdentifier:(NSString*)newPasswordIdentifier
-          confirmPasswordIdentifier:(NSString*)confirmPasswordIdentifier
-                  completionHandler:(void (^)(BOOL))completionHandler {
   // TODO(crbug.com/886583): form_signature, field_signature, max_length and
   // spec_priority in PGM::GeneratePassword are being refactored, passing 0 for
   // now to get a generic random password.
@@ -766,9 +751,6 @@
                 action:^{
                   [weakSelf
                       injectGeneratedPasswordForFormName:formName
-                                   newPasswordIdentifier:newPasswordIdentifier
-                               confirmPasswordIdentifier:
-                                   confirmPasswordIdentifier
                                        generatedPassword:nsPassword
                                        completionHandler:completionHandler];
                 }
@@ -795,23 +777,10 @@
       SysUTF16ToNSString(generation_data->new_password_element);
   NSString* confirmPasswordIdentifier =
       SysUTF16ToNSString(generation_data->confirmation_password_element);
-  [self injectGeneratedPasswordForFormName:formName
-                     newPasswordIdentifier:newPasswordIdentifier
-                 confirmPasswordIdentifier:confirmPasswordIdentifier
-                         generatedPassword:generatedPassword
-                         completionHandler:completionHandler];
-}
 
-- (void)injectGeneratedPasswordForFormName:(NSString*)formName
-                     newPasswordIdentifier:(NSString*)newPasswordIdentifier
-                 confirmPasswordIdentifier:(NSString*)confirmPasswordIdentifier
-                         generatedPassword:(NSString*)generatedPassword
-                         completionHandler:(void (^)(BOOL))completionHandler {
   auto generatedPasswordInjected = ^(BOOL success) {
     auto passwordPresaved = ^(BOOL found, const autofill::FormData& form) {
       if (found) {
-        // TODO(crbug.com/886583): set is_manually_triggered when we show a
-        // prompt.
         self.passwordManager->PresaveGeneratedPassword(
             self.passwordManagerDriver, form,
             SysNSStringToUTF16(generatedPassword),
diff --git a/ios/chrome/browser/passwords/password_tab_helper.h b/ios/chrome/browser/passwords/password_tab_helper.h
index c5e518c..33d92f8 100644
--- a/ios/chrome/browser/passwords/password_tab_helper.h
+++ b/ios/chrome/browser/passwords/password_tab_helper.h
@@ -40,12 +40,6 @@
   // Sets the PasswordController delegate.
   void SetPasswordControllerDelegate(id<PasswordControllerDelegate> delegate);
 
-  // Generate and offer to user a password for the given |formName| on given
-  // (optional) fields |newPasswordIdentifier| and |confirmPasswordIdentifier|.
-  void GenerateAndOfferPassword(NSString* formName,
-                                NSString* newPasswordIdentifier,
-                                NSString* confirmPasswordIdentifier);
-
   // Returns an object that can provide suggestions from the PasswordController.
   // May return nil.
   id<FormSuggestionProvider> GetSuggestionProvider();
diff --git a/ios/chrome/browser/passwords/password_tab_helper.mm b/ios/chrome/browser/passwords/password_tab_helper.mm
index bc4913a..94723d8 100644
--- a/ios/chrome/browser/passwords/password_tab_helper.mm
+++ b/ios/chrome/browser/passwords/password_tab_helper.mm
@@ -37,16 +37,6 @@
   controller_.delegate = delegate;
 }
 
-void PasswordTabHelper::GenerateAndOfferPassword(
-    NSString* formName,
-    NSString* newPasswordIdentifier,
-    NSString* confirmPasswordIdentifier) {
-  [controller_ generatePasswordForFormName:formName
-                     newPasswordIdentifier:newPasswordIdentifier
-                 confirmPasswordIdentifier:confirmPasswordIdentifier
-                         completionHandler:nil];
-}
-
 id<FormSuggestionProvider> PasswordTabHelper::GetSuggestionProvider() {
   return controller_.suggestionProvider;
 }
diff --git a/ios/chrome/browser/signin/BUILD.gn b/ios/chrome/browser/signin/BUILD.gn
index af9142f..6c9a54c 100644
--- a/ios/chrome/browser/signin/BUILD.gn
+++ b/ios/chrome/browser/signin/BUILD.gn
@@ -151,13 +151,13 @@
     ":signin",
     ":test_support",
     "//base",
-    "//components/browser_sync:test_support",
     "//components/pref_registry",
     "//components/prefs",
     "//components/signin/core/browser",
     "//components/signin/core/browser:internals_test_support",
     "//components/signin/ios/browser",
     "//components/signin/ios/browser:test_support",
+    "//components/sync:test_support_driver",
     "//components/sync_preferences",
     "//components/sync_preferences:test_support",
     "//google_apis",
diff --git a/ios/chrome/browser/signin/authentication_service_unittest.mm b/ios/chrome/browser/signin/authentication_service_unittest.mm
index 52a4b5cf..b5f0c28 100644
--- a/ios/chrome/browser/signin/authentication_service_unittest.mm
+++ b/ios/chrome/browser/signin/authentication_service_unittest.mm
@@ -6,11 +6,11 @@
 
 #include "base/run_loop.h"
 #include "base/strings/sys_string_conversions.h"
-#include "components/browser_sync/profile_sync_service_mock.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/signin/core/browser/signin_pref_names.h"
+#include "components/sync/driver/mock_sync_service.h"
 #include "components/sync_preferences/pref_service_mock_factory.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
@@ -26,7 +26,6 @@
 #import "ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.h"
 #include "ios/chrome/browser/signin/ios_chrome_signin_client.h"
 #include "ios/chrome/browser/signin/signin_client_factory.h"
-#include "ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #include "ios/chrome/browser/sync/sync_setup_service_mock.h"
@@ -80,6 +79,10 @@
           chrome_browser_state));
 }
 
+std::unique_ptr<KeyedService> BuildMockSyncService(web::BrowserState* context) {
+  return std::make_unique<syncer::MockSyncService>();
+}
+
 std::unique_ptr<KeyedService> BuildMockSyncSetupService(
     web::BrowserState* context) {
   ios::ChromeBrowserState* browser_state =
@@ -113,9 +116,8 @@
     builder.SetPrefService(CreatePrefService());
     builder.AddTestingFactory(SigninClientFactory::GetInstance(),
                               base::BindRepeating(&BuildFakeTestSigninClient));
-    builder.AddTestingFactory(
-        ProfileSyncServiceFactory::GetInstance(),
-        base::BindRepeating(&BuildMockProfileSyncService));
+    builder.AddTestingFactory(ProfileSyncServiceFactory::GetInstance(),
+                              base::BindRepeating(&BuildMockSyncService));
     builder.AddTestingFactory(SyncSetupServiceFactory::GetInstance(),
                               base::BindRepeating(&BuildMockSyncSetupService));
     builder.SetPrefService(CreatePrefService());
@@ -131,10 +133,8 @@
         std::make_unique<IdentityTestEnvironmentChromeBrowserStateAdaptor>(
             browser_state_.get());
 
-    profile_sync_service_mock_ =
-        static_cast<browser_sync::ProfileSyncServiceMock*>(
-            ProfileSyncServiceFactory::GetForBrowserState(
-                browser_state_.get()));
+    mock_sync_service_ = static_cast<syncer::MockSyncService*>(
+        ProfileSyncServiceFactory::GetForBrowserState(browser_state_.get()));
     sync_setup_service_mock_ = static_cast<SyncSetupServiceMock*>(
         SyncSetupServiceFactory::GetForBrowserState(browser_state_.get()));
     CreateAuthenticationService();
@@ -161,7 +161,7 @@
   }
 
   void SetExpectationsForSignIn() {
-    EXPECT_CALL(*profile_sync_service_mock_->GetUserSettingsMock(),
+    EXPECT_CALL(*mock_sync_service_->GetMockUserSettings(),
                 SetSyncRequested(true));
     EXPECT_CALL(*sync_setup_service_mock_, PrepareForFirstSyncSetup());
   }
@@ -241,7 +241,7 @@
   IOSChromeScopedTestingChromeBrowserStateManager scoped_browser_state_manager_;
   std::unique_ptr<TestChromeBrowserState> browser_state_;
   ios::FakeChromeIdentityService* identity_service_;
-  browser_sync::ProfileSyncServiceMock* profile_sync_service_mock_;
+  syncer::MockSyncService* mock_sync_service_;
   SyncSetupServiceMock* sync_setup_service_mock_;
   std::unique_ptr<AuthenticationService> authentication_service_;
   std::unique_ptr<IdentityTestEnvironmentChromeBrowserStateAdaptor>
@@ -309,7 +309,7 @@
       .WillOnce(Invoke(
           sync_setup_service_mock_,
           &SyncSetupServiceMock::SyncSetupServiceHasFinishedInitialSetup));
-  EXPECT_CALL(*profile_sync_service_mock_, GetDisableReasons())
+  EXPECT_CALL(*mock_sync_service_, GetDisableReasons())
       .WillOnce(Return(syncer::SyncService::DISABLE_REASON_USER_CHOICE));
 
   CreateAuthenticationService();
@@ -330,7 +330,7 @@
   EXPECT_CALL(*sync_setup_service_mock_, HasFinishedInitialSetup())
       .WillOnce(Return(false));
   // Expect a call to disable sync as part of the sign out process.
-  EXPECT_CALL(*profile_sync_service_mock_, StopAndClear());
+  EXPECT_CALL(*mock_sync_service_, StopAndClear());
 
   CreateAuthenticationService();
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_delegate.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_delegate.h
index d2a46dd..a990f01 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_delegate.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_delegate.h
@@ -33,9 +33,6 @@
              passwordField:(BOOL)passwordField
              requiresHTTPS:(BOOL)requiresHTTPS;
 
-// Generate and offer to user a password that matches current form.
-- (void)generateAndOfferPassword;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_MANUAL_FILL_CONTENT_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
index 82d5cb9..a6ca234 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
@@ -69,9 +69,6 @@
 // The last seen focused element identifier.
 @property(nonatomic, assign) std::string lastFocusedElementIdentifier;
 
-// The form name of the last seen focused element.
-@property(nonatomic, assign) std::string lastFocusedFormName;
-
 // The view controller this object was initialized with.
 @property(weak, nonatomic, nullable, readonly)
     UIViewController* baseViewController;
@@ -125,15 +122,6 @@
   }
 }
 
-- (void)generateAndOfferPassword {
-  if (![self isLastFocusedElementPasswordField])
-    return;
-  web::WebState* webState = self.webStateList->GetActiveWebState();
-  PasswordTabHelper::FromWebState(webState)->GenerateAndOfferPassword(
-      base::SysUTF8ToNSString(self.lastFocusedFormName),
-      base::SysUTF8ToNSString(self.lastFocusedElementIdentifier), nil);
-}
-
 #pragma mark - FormActivityObserver
 
 - (void)webState:(web::WebState*)webState
@@ -146,7 +134,6 @@
       autofill::IsContextSecureForWebState(webState);
   self.lastFocusedElementPasswordField = params.field_type == "password";
   self.lastFocusedElementIdentifier = params.field_identifier;
-  self.lastFocusedFormName = params.form_name;
   if (autofill::switches::IsAutofillIFrameMessagingEnabled()) {
     DCHECK(frame);
     self.lastFocusedElementFrameIdentifier = frame->GetFrameId();
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.mm
index c5bc399..f6968de 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.mm
@@ -185,23 +185,6 @@
         [[NSMutableArray alloc] init];
     __weak __typeof(self) weakSelf = self;
 
-    if (features::IsAutomaticPasswordGenerationEnabled() &&
-        [self.contentDelegate canUserInjectInPasswordField:YES
-                                             requiresHTTPS:YES]) {
-      NSString* generatePasswordTitleString =
-          l10n_util::GetNSString(IDS_IOS_SUGGEST_PASSWORD);
-      auto generatePasswordItem = [[ManualFillActionItem alloc]
-          initWithTitle:generatePasswordTitleString
-                 action:^{
-                   base::RecordAction(base::UserMetricsAction(
-                       "ManualFallback_Password_OpenSuggestPassword"));
-                   [weakSelf generateAndOfferPassword];
-                 }];
-      generatePasswordItem.accessibilityIdentifier =
-          manual_fill::SuggestPasswordAccessibilityIdentifier;
-      [actions addObject:generatePasswordItem];
-    }
-
     // TODO(crbug.com/908776): fix or wait until iOS 11.2- is deprecated.
     if (@available(iOS 11.3, *)) {
       NSString* otherPasswordsTitleString = l10n_util::GetNSString(
@@ -262,8 +245,4 @@
                              requiresHTTPS:requiresHTTPS];
 }
 
-- (void)generateAndOfferPassword {
-  [self.contentDelegate generateAndOfferPassword];
-}
-
 @end
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
index 150f8998..fcc0a024 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
@@ -48,6 +48,7 @@
 #import "ios/chrome/browser/ui/util/rtl_geometry.h"
 #import "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_service_factory.h"
 #import "ios/chrome/common/favicon/favicon_attributes.h"
@@ -1058,9 +1059,8 @@
       base::UserMetricsAction("MobileBookmarkManagerEntryOpened"));
   web::NavigationManager::WebLoadParams params(url);
   params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
-  ChromeLoadParams chromeParams(params);
   UrlLoadingServiceFactory::GetForBrowserState(self.browserState)
-      ->LoadUrlInCurrentTab(chromeParams);
+      ->Load(UrlLoadParams::InCurrentTab(params));
 }
 
 - (void)addNewFolder {
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.mm
index 50e66c3..486ab649 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.mm
@@ -35,6 +35,7 @@
 #import "ios/chrome/browser/ui/table_view/table_view_presentation_controller.h"
 #import "ios/chrome/browser/ui/table_view/table_view_presentation_controller_delegate.h"
 #include "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_service_factory.h"
 #import "ios/chrome/browser/url_loading/url_loading_util.h"
@@ -547,9 +548,8 @@
   }
   web::NavigationManager::WebLoadParams params(url);
   params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
-  ChromeLoadParams chromeParams(params);
   UrlLoadingServiceFactory::GetForBrowserState(_currentBrowserState)
-      ->LoadUrlInCurrentTab(chromeParams);
+      ->Load(UrlLoadParams::InCurrentTab(params));
 }
 
 - (void)openURLInNewTab:(const GURL&)url
@@ -557,14 +557,8 @@
            inBackground:(BOOL)inBackground {
   // TODO(crbug.com/695749):  Open bookmarklet in new tab doesn't work.  See how
   // to deal with this later.
-  OpenNewTabCommand* command =
-      [[OpenNewTabCommand alloc] initWithURL:url
-                                    referrer:web::Referrer()
-                                 inIncognito:inIncognito
-                                inBackground:inBackground
-                                    appendTo:kLastTab];
   UrlLoadingServiceFactory::GetForBrowserState(_currentBrowserState)
-      ->LoadUrlInNewTab(command);
+      ->Load(UrlLoadParams::InNewTab(url, inIncognito, inBackground, kLastTab));
 }
 
 @end
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 113fb066..280fae1 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -162,6 +162,7 @@
 #import "ios/chrome/browser/url_loading/url_loading_notifier.h"
 #import "ios/chrome/browser/url_loading/url_loading_notifier_factory.h"
 #import "ios/chrome/browser/url_loading/url_loading_observer_bridge.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_service_factory.h"
 #import "ios/chrome/browser/url_loading/url_loading_util.h"
@@ -3185,15 +3186,13 @@
         if (!strongSelf)
           return;
 
-        OpenNewTabCommand* command =
-            [[OpenNewTabCommand alloc] initWithURL:link
-                                          referrer:referrer
-                                       inIncognito:strongSelf.isOffTheRecord
-                                      inBackground:YES
-                                          appendTo:kCurrentTab];
-        command.originPoint = originPoint;
+        UrlLoadParams* params = UrlLoadParams::InNewTab(
+            link, referrer,
+            /* in_incognito */ strongSelf.isOffTheRecord,
+            /* in_background */ YES, kCurrentTab);
+        params->origin_point = originPoint;
         UrlLoadingServiceFactory::GetForBrowserState(strongSelf.browserState)
-            ->LoadUrlInNewTab(command);
+            ->Load(params);
       };
       [_contextMenuCoordinator addItemWithTitle:title action:action];
       if (!_isOffTheRecord) {
@@ -3207,14 +3206,12 @@
 
           Record(ACTION_OPEN_IN_INCOGNITO_TAB, isImage, isLink);
 
-          OpenNewTabCommand* command =
-              [[OpenNewTabCommand alloc] initWithURL:link
-                                            referrer:referrer
-                                         inIncognito:YES
-                                        inBackground:NO
-                                            appendTo:kCurrentTab];
+          UrlLoadParams* params =
+              UrlLoadParams::InNewTab(link, referrer,
+                                      /* in_incognito */ YES,
+                                      /* in_background */ NO, kCurrentTab);
           UrlLoadingServiceFactory::GetForBrowserState(strongSelf.browserState)
-              ->LoadUrlInNewTab(command);
+              ->Load(params);
         };
         [_contextMenuCoordinator addItemWithTitle:title action:action];
       }
@@ -3270,9 +3267,8 @@
         return;
 
       Record(ACTION_OPEN_IMAGE, isImage, isLink);
-      ChromeLoadParams params(imageUrl);
       UrlLoadingServiceFactory::GetForBrowserState(strongSelf.browserState)
-          ->LoadUrlInCurrentTab(params);
+          ->Load(UrlLoadParams::InCurrentTab(imageUrl));
     };
     [_contextMenuCoordinator addItemWithTitle:title action:action];
     // Open Image In New Tab.
@@ -3284,16 +3280,13 @@
       if (!strongSelf)
         return;
 
-      OpenNewTabCommand* command =
-          [[OpenNewTabCommand alloc] initWithURL:imageUrl
-                                        referrer:referrer
-                                     inIncognito:strongSelf.isOffTheRecord
-                                    inBackground:YES
-
-                                        appendTo:kCurrentTab];
-      command.originPoint = originPoint;
+      UrlLoadParams* params =
+          UrlLoadParams::InNewTab(imageUrl, referrer,
+                                  /* in_incognito */ strongSelf.isOffTheRecord,
+                                  /* in_background */ YES, kCurrentTab);
+      params->origin_point = originPoint;
       UrlLoadingServiceFactory::GetForBrowserState(strongSelf.browserState)
-          ->LoadUrlInNewTab(command);
+          ->Load(params);
     };
     [_contextMenuCoordinator addItemWithTitle:title action:action];
 
@@ -3419,7 +3412,7 @@
                               inBackground:NO];
   } else {
     UrlLoadingServiceFactory::GetForBrowserState(self.browserState)
-        ->LoadUrlInCurrentTab(ChromeLoadParams(loadParams));
+        ->Load(UrlLoadParams::InCurrentTab(loadParams));
   }
 }
 
@@ -3428,7 +3421,7 @@
 // TODO(crbug.com/907527): consider moving these separate functional blurbs
 // closer to their main component (using localized observers)
 
-- (void)tabWillOpenURL:(GURL)URL
+- (void)tabWillLoadURL:(GURL)URL
         transitionType:(ui::PageTransition)transitionType {
   [_bookmarkInteractionController dismissBookmarkModalControllerAnimated:YES];
 
@@ -3444,10 +3437,10 @@
   }
 }
 
-- (void)tabDidOpenURL:(GURL)URL
+- (void)tabDidLoadURL:(GURL)URL
        transitionType:(ui::PageTransition)transitionType {
   // Deactivate the NTP immediately on a load to hide the NTP quickly, but
-  // after calling UrlLoadingService::LoadUrlInCurrentTab.  Otherwise, if the
+  // after calling UrlLoadingService::Load.  Otherwise, if the
   // webState has never been visible (such as during startup with an NTP), it's
   // possible the webView can trigger a unnecessary load for chrome://newtab.
   if (URL.GetOrigin() != kChromeUINewTabURL) {
@@ -3462,7 +3455,7 @@
   }
 }
 
-- (void)newTabWillOpenURL:(GURL)URL inIncognito:(BOOL)inIncognito {
+- (void)newTabWillLoadURL:(GURL)URL inIncognito:(BOOL)inIncognito {
   if (!IsHelpURL(URL)) {
     // Send either the "New Tab Opened" or "New Incognito Tab" opened to the
     // feature_engagement::Tracker based on |inIncognito|.
@@ -4277,14 +4270,11 @@
 
 - (void)showHelpPage {
   GURL helpUrl(l10n_util::GetStringUTF16(IDS_IOS_TOOLS_MENU_HELP_URL));
-  OpenNewTabCommand* command =
-      [[OpenNewTabCommand alloc] initWithURL:helpUrl
-                                    referrer:web::Referrer()
-                                 inIncognito:NO
-                                inBackground:NO
-                                    appendTo:kCurrentTab];
-  UrlLoadingServiceFactory::GetForBrowserState(self.browserState)
-      ->LoadUrlInNewTab(command);
+  UrlLoadParams* params =
+      UrlLoadParams::InNewTab(helpUrl,
+                              /* in_incognito */ NO,
+                              /* in_background */ NO, kCurrentTab);
+  UrlLoadingServiceFactory::GetForBrowserState(self.browserState)->Load(params);
 }
 
 - (void)showBookmarksManager {
@@ -4322,9 +4312,8 @@
 - (void)navigateToMemexTabSwitcher {
   // TODO(crbug.com/799601): Delete this once its not needed.
   const GURL memexURL("https://chrome-memex.appspot.com");
-  ChromeLoadParams params(memexURL);
   UrlLoadingServiceFactory::GetForBrowserState(self.browserState)
-      ->LoadUrlInCurrentTab(params);
+      ->Load(UrlLoadParams::InCurrentTab(memexURL));
 }
 
 - (void)prepareForPopupMenuPresentation:(PopupMenuCommandType)type {
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
index c3dd9d37..8831a2a 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
@@ -41,6 +41,7 @@
 #import "ios/chrome/browser/ui/toolbar/public/omnibox_focuser.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
@@ -236,8 +237,7 @@
       web::Referrer(GURL(ntp_snippets::GetContentSuggestionsReferrerURL()),
                     web::ReferrerPolicyDefault);
   params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
-  ChromeLoadParams chromeParams(params);
-  _urlLoadingService->LoadUrlInCurrentTab(chromeParams);
+  _urlLoadingService->Load(UrlLoadParams::InCurrentTab(params));
   [self.NTPMetrics recordAction:new_tab_page_uma::ACTION_OPENED_SUGGESTION];
 }
 
@@ -280,8 +280,7 @@
 
   web::NavigationManager::WebLoadParams params(mostVisitedItem.URL);
   params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
-  ChromeLoadParams chromeParams(params);
-  _urlLoadingService->LoadUrlInCurrentTab(chromeParams);
+  _urlLoadingService->Load(UrlLoadParams::InCurrentTab(params));
 }
 
 - (void)displayContextMenuForSuggestion:(CollectionViewItem*)item
@@ -342,13 +341,11 @@
   [self.NTPMetrics recordAction:new_tab_page_uma::ACTION_OPENED_PROMO];
 
   if (notificationPromo->IsURLPromo()) {
-    OpenNewTabCommand* command =
-        [[OpenNewTabCommand alloc] initWithURL:notificationPromo->url()
-                                      referrer:web::Referrer()
-                                   inIncognito:NO
-                                  inBackground:NO
-                                      appendTo:kCurrentTab];
-    _urlLoadingService->LoadUrlInNewTab(command);
+    UrlLoadParams* params =
+        UrlLoadParams::InNewTab(notificationPromo->url(),
+                                /* in_incognito */ NO,
+                                /* in_background */ NO, kCurrentTab);
+    _urlLoadingService->Load(params);
     return;
   }
 
@@ -368,8 +365,7 @@
   if (NTPHelper && NTPHelper->IgnoreLoadRequests())
     return;
   GURL URL(kNTPHelpURL);
-  ChromeLoadParams params(URL);
-  _urlLoadingService->LoadUrlInCurrentTab(params);
+  _urlLoadingService->Load(UrlLoadParams::InCurrentTab(URL));
   [self.NTPMetrics recordAction:new_tab_page_uma::ACTION_OPENED_LEARN_MORE];
 }
 
@@ -544,19 +540,17 @@
                 incognito:(BOOL)incognito
               originPoint:(CGPoint)originPoint {
   // Open the tab in background if it is non-incognito only.
-  OpenNewTabCommand* command =
-      [[OpenNewTabCommand alloc] initWithURL:URL
-                                    referrer:web::Referrer()
-                                 inIncognito:incognito
-                                inBackground:!incognito
-                                    appendTo:kCurrentTab];
-  command.originPoint = originPoint;
+  UrlLoadParams* params =
+      UrlLoadParams::InNewTab(URL,
+                              /* in_incognito */ incognito,
+                              /* in_background */ !incognito, kCurrentTab);
+  params->origin_point = originPoint;
   if (incognito) {
     // Unfocus the omnibox if the new page should be opened in incognito to
     // prevent staying stuck.
     [self.dispatcher cancelOmniboxEdit];
   }
-  _urlLoadingService->LoadUrlInNewTab(command);
+  _urlLoadingService->Load(params);
 }
 
 // Logs a histogram due to a Most Visited item being opened.
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
index 0ca89764..4a03481 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
@@ -67,6 +67,9 @@
       [[InfobarBannerViewController alloc] initWithDelegate:self];
   self.bannerViewController.titleText =
       base::SysUTF16ToNSString(self.passwordInfoBarDelegate->GetMessageText());
+  NSString* username = self.passwordInfoBarDelegate->GetUserNameText();
+  self.bannerViewController.subTitleText =
+      [NSString stringWithFormat:@"%@ •••••••••", username];
   self.bannerViewController.buttonText =
       base::SysUTF16ToNSString(self.passwordInfoBarDelegate->GetButtonLabel(
           ConfirmInfoBarDelegate::BUTTON_OK));
@@ -177,6 +180,9 @@
   self.modalViewController.title =
       base::SysUTF16ToNSString(self.passwordInfoBarDelegate->GetMessageText());
   self.modalViewController.infobarModalDelegate = self;
+  self.modalViewController.username =
+      self.passwordInfoBarDelegate->GetUserNameText();
+  self.modalViewController.URL = self.passwordInfoBarDelegate->GetURLHostText();
 
   TableViewNavigationController* navigationController =
       [[TableViewNavigationController alloc]
diff --git a/ios/chrome/browser/ui/infobars/modals/BUILD.gn b/ios/chrome/browser/ui/infobars/modals/BUILD.gn
index 6729466..9a3a909 100644
--- a/ios/chrome/browser/ui/infobars/modals/BUILD.gn
+++ b/ios/chrome/browser/ui/infobars/modals/BUILD.gn
@@ -13,7 +13,9 @@
   ]
   deps = [
     "//base",
+    "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser/ui/table_view",
     "//ios/chrome/browser/ui/table_view:styler",
+    "//ui/base",
   ]
 }
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.h b/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.h
index 775e0ae..a524c21 100644
--- a/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.h
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.h
@@ -14,7 +14,11 @@
 @interface InfobarPasswordTableViewController : ChromeTableViewController
 
 // InfobarModalDelegate for this ViewController.
-@property(strong, nonatomic) id<InfobarModalDelegate> infobarModalDelegate;
+@property(nonatomic, strong) id<InfobarModalDelegate> infobarModalDelegate;
+// The username being displayed in the InfobarModal.
+@property(nonatomic, copy) NSString* username;
+// The URL being displayed in the InfobarModal.
+@property(nonatomic, copy) NSString* URL;
 
 @end
 
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.mm b/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.mm
index 13d15dad..2065d7b 100644
--- a/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.mm
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.mm
@@ -11,6 +11,8 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -69,27 +71,33 @@
 
   SettingsDetailItem* URLDetailItem =
       [[SettingsDetailItem alloc] initWithType:ItemTypeURL];
-  URLDetailItem.text = @"URL";
-  URLDetailItem.detailText = @"www.twitter.com";
+  URLDetailItem.text = l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_SITE);
+  URLDetailItem.detailText = self.URL;
   [model addItem:URLDetailItem
       toSectionWithIdentifier:SectionIdentifierContent];
 
   SettingsDetailItem* usernameDetailItem =
       [[SettingsDetailItem alloc] initWithType:ItemTypeUsername];
-  usernameDetailItem.text = @"Username";
-  usernameDetailItem.detailText = @"martijnb@google.com";
+  usernameDetailItem.text =
+      l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME);
+  usernameDetailItem.detailText = self.username;
   [model addItem:usernameDetailItem
       toSectionWithIdentifier:SectionIdentifierContent];
 
   SettingsDetailItem* passwordDetailItem =
       [[SettingsDetailItem alloc] initWithType:ItemTypePassword];
-  passwordDetailItem.text = @"Password";
-  passwordDetailItem.detailText = @"********";
+  passwordDetailItem.text =
+      l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD);
+  // TODO(crbug.com/927064): Set the number of dots depending on Password
+  // length?
+  passwordDetailItem.detailText = @"•••••••••";
   [model addItem:passwordDetailItem
       toSectionWithIdentifier:SectionIdentifierContent];
 
   TableViewTextButtonItem* savePasswordButtonItem =
       [[TableViewTextButtonItem alloc] initWithType:ItemTypeSavePassword];
+  // TODO(crbug.com/927064): Create IDS String for this once we're sure about
+  // the exact text.
   savePasswordButtonItem.buttonText = @"Save Password";
   [model addItem:savePasswordButtonItem
       toSectionWithIdentifier:SectionIdentifierContent];
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index 39aa1e6..e8900da 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -38,6 +38,7 @@
 #include "ios/chrome/browser/ui/omnibox/web_omnibox_edit_controller_impl.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/util/pasteboard_util.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_service_factory.h"
 #import "ios/chrome/browser/url_loading/url_loading_util.h"
@@ -250,10 +251,8 @@
         [[self variationHeadersForURL:url] mutableCopy];
     [combinedExtraHeaders addEntriesFromDictionary:params.extra_headers];
     params.extra_headers = [combinedExtraHeaders copy];
-    ChromeLoadParams chromeParams(params);
-    chromeParams.disposition = disposition;
     UrlLoadingServiceFactory::GetForBrowserState(self.browserState)
-        ->LoadUrlInCurrentTab(chromeParams);
+        ->Load(UrlLoadParams::InCurrentTab(params, disposition));
 
     if (google_util::IsGoogleSearchUrl(url)) {
       UMA_HISTOGRAM_ENUMERATION(
@@ -430,9 +429,8 @@
     web::NavigationManager::WebLoadParams params(searchURL);
     params.transition_type = ui::PageTransitionFromInt(
         ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
-    ChromeLoadParams chromeParams(params);
     UrlLoadingServiceFactory::GetForBrowserState(self.browserState)
-        ->LoadUrlInCurrentTab(chromeParams);
+        ->Load(UrlLoadParams::InCurrentTab(params));
   }
 }
 
diff --git a/ios/chrome/browser/ui/ntp/incognito_view.mm b/ios/chrome/browser/ui/ntp/incognito_view.mm
index b1f2def7..715a660 100644
--- a/ios/chrome/browser/ui/ntp/incognito_view.mm
+++ b/ios/chrome/browser/ui/ntp/incognito_view.mm
@@ -12,6 +12,7 @@
 #include "ios/chrome/browser/ui/util/rtl_geometry.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/common/string_util.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
@@ -348,8 +349,8 @@
 
 // Triggers a navigation to the help page.
 - (void)learnMoreButtonPressed {
-  ChromeLoadParams params(GetUrlWithLang(GURL(kLearnMoreIncognitoUrl)));
-  _urlLoadingService->LoadUrlInCurrentTab(params);
+  _urlLoadingService->Load(UrlLoadParams::InCurrentTab(
+      GetUrlWithLang(GURL(kLearnMoreIncognitoUrl))));
 }
 
 // Adds views containing the text of the incognito page to |_stackView|.
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.mm
index cfb6084..e3f79552 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.mm
@@ -15,6 +15,7 @@
 #import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_consumer.h"
 #import "ios/chrome/browser/ui/toolbar/public/omnibox_focuser.h"
 #import "ios/chrome/browser/ui/url_loader.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
@@ -109,9 +110,8 @@
 - (void)openMostVisitedItem:(ShortcutsMostVisitedItem*)item {
   web::NavigationManager::WebLoadParams params(item.URL);
   params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
-  ChromeLoadParams chromeParams(params);
   [self.dispatcher cancelOmniboxEdit];
-  _loadingService->LoadUrlInCurrentTab(chromeParams);
+  _loadingService->Load(UrlLoadParams::InCurrentTab(params));
 }
 
 - (void)openBookmarks {
diff --git a/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm b/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm
index 49a3bb5..45687d84 100644
--- a/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm
+++ b/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm
@@ -23,6 +23,7 @@
 #import "ios/chrome/browser/ui/page_info/page_info_view_controller.h"
 #import "ios/chrome/browser/ui/page_info/requirements/page_info_presentation.h"
 #import "ios/chrome/browser/ui/page_info/requirements/page_info_reloading.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_service_factory.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
@@ -155,15 +156,11 @@
 }
 
 - (void)showSecurityHelpPage {
-  OpenNewTabCommand* command =
-      [[OpenNewTabCommand alloc] initWithURL:GURL(kPageInfoHelpCenterURL)
-                                    referrer:web::Referrer()
-                                 inIncognito:self.browserState->IsOffTheRecord()
-                                inBackground:NO
-                                    appendTo:kLastTab];
-
-  UrlLoadingServiceFactory::GetForBrowserState(self.browserState)
-      ->LoadUrlInNewTab(command);
+  UrlLoadParams* params = UrlLoadParams::InNewTab(
+      GURL(kPageInfoHelpCenterURL),
+      /* in_incognito */ self.browserState->IsOffTheRecord(),
+      /* in_background */ NO, kLastTab);
+  UrlLoadingServiceFactory::GetForBrowserState(self.browserState)->Load(params);
   [self hidePageInfo];
 }
 
diff --git a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm
index 4318f26..036edb3f 100644
--- a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm
@@ -22,6 +22,7 @@
 #include "ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.h"
 #import "ios/chrome/browser/ui/toolbar/public/features.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_service_factory.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
@@ -423,9 +424,8 @@
             ui::PageTransition transition) {
         web::NavigationManager::WebLoadParams params(replacementURL);
         params.transition_type = transition;
-        ChromeLoadParams chromeParams(params);
         UrlLoadingServiceFactory::GetForBrowserState(self.browserState)
-            ->LoadUrlInCurrentTab(chromeParams);
+            ->Load(UrlLoadParams::InCurrentTab(params));
         [self cancelOmniboxEdit];
       };
   load_GURL_from_location_bar_swizzler_.reset(new ScopedBlockSwizzler(
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm b/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm
index efd85a74..8a3fdd5 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm
@@ -33,6 +33,7 @@
 #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller_constants.h"
 #import "ios/chrome/browser/ui/table_view/table_view_presentation_controller.h"
 #import "ios/chrome/browser/ui/util/pasteboard_util.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_service_factory.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -366,23 +367,20 @@
   if (newTab) {
     web::Referrer referrer = web::Referrer(GURL(kReadingListReferrerURL),
                                            web::ReferrerPolicyDefault);
-    OpenNewTabCommand* command =
-        [[OpenNewTabCommand alloc] initWithURL:loadURL
-                                    virtualURL:entryURL
-                                      referrer:referrer
-                                   inIncognito:incognito
-                                  inBackground:NO
-                                      appendTo:kLastTab];
+    UrlLoadParams* params =
+        UrlLoadParams::InNewTab(loadURL, entryURL, referrer,
+                                /* in_incognito */ incognito,
+                                /* in_background */ NO, kLastTab);
     UrlLoadingServiceFactory::GetForBrowserState(self.browserState)
-        ->LoadUrlInNewTab(command);
+        ->Load(params);
   } else {
-    web::NavigationManager::WebLoadParams params(loadURL);
-    params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
-    params.referrer = web::Referrer(GURL(kReadingListReferrerURL),
-                                    web::ReferrerPolicyDefault);
-    ChromeLoadParams chromeParams(params);
+    web::NavigationManager::WebLoadParams web_params(loadURL);
+    web_params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
+    web_params.referrer = web::Referrer(GURL(kReadingListReferrerURL),
+                                        web::ReferrerPolicyDefault);
+    UrlLoadParams* params = UrlLoadParams::InCurrentTab(web_params);
     UrlLoadingServiceFactory::GetForBrowserState(self.browserState)
-        ->LoadUrlInCurrentTab(chromeParams);
+        ->Load(params);
   }
 
   [self stop];
diff --git a/ios/chrome/browser/ui/static_content/static_html_view_controller.mm b/ios/chrome/browser/ui/static_content/static_html_view_controller.mm
index a76349c..be6900f 100644
--- a/ios/chrome/browser/ui/static_content/static_html_view_controller.mm
+++ b/ios/chrome/browser/ui/static_content/static_html_view_controller.mm
@@ -12,6 +12,7 @@
 #include "base/mac/bundle_locations.h"
 #include "base/mac/foundation_util.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_service_factory.h"
 #import "ios/web/public/navigation_manager.h"
@@ -233,11 +234,11 @@
   // if they are issued by the main frame.
   if (fromMainFrame) {
     dispatch_async(dispatch_get_main_queue(), ^{
-      ChromeLoadParams params(net::GURLWithNSURL([request URL]));
       ios::ChromeBrowserState* chrome_browser_state =
           ios::ChromeBrowserState::FromBrowserState(browserState_);
       UrlLoadingServiceFactory::GetForBrowserState(chrome_browser_state)
-          ->LoadUrlInCurrentTab(params);
+          ->Load(
+              UrlLoadParams::InCurrentTab(net::GURLWithNSURL([request URL])));
     });
   }
   return NO;
diff --git a/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h b/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h
index 3df3168..27d1176 100644
--- a/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h
+++ b/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h
@@ -21,6 +21,8 @@
       web::WebUIIOS* web_ui,
       const GURL& url) const override;
 
+  bool HasWebUIIOSControllerForURL(const GURL& url) const override;
+
   static ChromeWebUIIOSControllerFactory* GetInstance();
 
  protected:
diff --git a/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm b/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm
index 7bc8aaf..255234b9 100644
--- a/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm
+++ b/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm
@@ -55,8 +55,7 @@
 // Returns a function that can be used to create the right type of WebUIIOS for
 // a tab, based on its URL. Returns NULL if the URL doesn't have WebUIIOS
 // associated with it.
-WebUIIOSFactoryFunction GetWebUIIOSFactoryFunction(WebUIIOS* web_ui,
-                                                   const GURL& url) {
+WebUIIOSFactoryFunction GetWebUIIOSFactoryFunction(const GURL& url) {
   // This will get called a lot to check all URLs, so do a quick check of other
   // schemes to filter out most URLs.
   if (!url.SchemeIs(kChromeUIScheme))
@@ -104,11 +103,16 @@
 
 }  // namespace
 
+bool ChromeWebUIIOSControllerFactory::HasWebUIIOSControllerForURL(
+    const GURL& url) const {
+  return GetWebUIIOSFactoryFunction(url);
+}
+
 std::unique_ptr<WebUIIOSController>
 ChromeWebUIIOSControllerFactory::CreateWebUIIOSControllerForURL(
     WebUIIOS* web_ui,
     const GURL& url) const {
-  WebUIIOSFactoryFunction function = GetWebUIIOSFactoryFunction(web_ui, url);
+  WebUIIOSFactoryFunction function = GetWebUIIOSFactoryFunction(url);
   if (!function)
     return nullptr;
 
diff --git a/ios/chrome/browser/url_loading/test_url_loading_service.h b/ios/chrome/browser/url_loading/test_url_loading_service.h
index effce624a..8099bc9 100644
--- a/ios/chrome/browser/url_loading/test_url_loading_service.h
+++ b/ios/chrome/browser/url_loading/test_url_loading_service.h
@@ -23,12 +23,10 @@
   // Switches to a tab that matches |params.web_params| or opens in a new tab.
   void SwitchToTab(UrlLoadParams* params) override;
 
-  // Opens a url based on |chrome_params|.
-  // TODO(crbug.com/907527): to be deprecated, use OpenUrl.
+  // Opens a url based on |params| in current tab.
   void LoadUrlInCurrentTab(UrlLoadParams* params) override;
 
-  // Opens a url based on |command| in a new tab.
-  // TODO(crbug.com/907527): to be deprecated, use OpenUrl.
+  // Opens a url based on |params| in a new tab.
   void LoadUrlInNewTab(UrlLoadParams* params) override;
 };
 
diff --git a/ios/chrome/browser/url_loading/url_loading_notifier.h b/ios/chrome/browser/url_loading/url_loading_notifier.h
index 8f3509ad..6e27e702 100644
--- a/ios/chrome/browser/url_loading/url_loading_notifier.h
+++ b/ios/chrome/browser/url_loading/url_loading_notifier.h
@@ -28,15 +28,15 @@
   // Removes |observer| from the list of observers.
   void RemoveObserver(UrlLoadingObserverBridge* observer);
 
-  // The loader will open |url| in the current tab. Next state will be
-  // one of: TabFailedToOpenUrl, TabDidPrerenderUrl,
-  // TabDidReloadUrl or TabDidOpenUrl.
-  void TabWillOpenUrl(const GURL& url, ui::PageTransition transition_type);
+  // The loader will load |url| in the current tab. Next state will be
+  // one of: TabFailedToLoadUrl, TabDidPrerenderUrl,
+  // TabDidReloadUrl or TabDidLoadUrl.
+  void TabWillLoadUrl(const GURL& url, ui::PageTransition transition_type);
 
-  // The loader didn't succeed opening the requested |url|. Reason
+  // The loader didn't succeed loading the requested |url|. Reason
   // can, for example be an incognito mismatch or an induced crash.
-  // It is possible that the url was opened, but in another tab.
-  void TabFailedToOpenUrl(const GURL& url, ui::PageTransition transition_type);
+  // It is possible that the url was loaded, but in another tab.
+  void TabFailedToLoadUrl(const GURL& url, ui::PageTransition transition_type);
 
   // The loader replaced the load with a prerendering.
   void TabDidPrerenderUrl(const GURL& url, ui::PageTransition transition_type);
@@ -45,14 +45,14 @@
   void TabDidReloadUrl(const GURL& url, ui::PageTransition transition_type);
 
   // The loader initiated the |url| loading successfully.
-  void TabDidOpenUrl(const GURL& url, ui::PageTransition transition_type);
+  void TabDidLoadUrl(const GURL& url, ui::PageTransition transition_type);
 
-  // The loader will open |url| in a new tab. Next state will be
-  // NewTabDidOpenUrl.
-  void NewTabWillOpenUrl(const GURL& url, bool in_incognito);
+  // The loader will load |url| in a new tab. Next state will be
+  // NewTabDidLoadUrl.
+  void NewTabWillLoadUrl(const GURL& url, bool in_incognito);
 
   // The loader initiated the |url| loading in a new tab successfully.
-  void NewTabDidOpenUrl(const GURL& url, bool in_incognito);
+  void NewTabDidLoadUrl(const GURL& url, bool in_incognito);
 
   // The loader will switch to an existing tab with |URL| instead of loading it.
   // Next state will be: DidSwitchToTabWithUrl.
diff --git a/ios/chrome/browser/url_loading/url_loading_notifier.mm b/ios/chrome/browser/url_loading/url_loading_notifier.mm
index 55ae914..34599b54 100644
--- a/ios/chrome/browser/url_loading/url_loading_notifier.mm
+++ b/ios/chrome/browser/url_loading/url_loading_notifier.mm
@@ -21,17 +21,17 @@
   observers_.RemoveObserver(observer);
 }
 
-void UrlLoadingNotifier::TabWillOpenUrl(const GURL& url,
+void UrlLoadingNotifier::TabWillLoadUrl(const GURL& url,
                                         ui::PageTransition transition_type) {
   for (auto& observer : observers_)
-    observer.TabWillOpenUrl(url, transition_type);
+    observer.TabWillLoadUrl(url, transition_type);
 }
 
-void UrlLoadingNotifier::TabFailedToOpenUrl(
+void UrlLoadingNotifier::TabFailedToLoadUrl(
     const GURL& url,
     ui::PageTransition transition_type) {
   for (auto& observer : observers_)
-    observer.TabFailedToOpenUrl(url, transition_type);
+    observer.TabFailedToLoadUrl(url, transition_type);
 }
 
 void UrlLoadingNotifier::TabDidPrerenderUrl(
@@ -47,20 +47,20 @@
     observer.TabDidReloadUrl(url, transition_type);
 }
 
-void UrlLoadingNotifier::TabDidOpenUrl(const GURL& url,
+void UrlLoadingNotifier::TabDidLoadUrl(const GURL& url,
                                        ui::PageTransition transition_type) {
   for (auto& observer : observers_)
-    observer.TabDidOpenUrl(url, transition_type);
+    observer.TabDidLoadUrl(url, transition_type);
 }
 
-void UrlLoadingNotifier::NewTabWillOpenUrl(const GURL& url, bool in_incognito) {
+void UrlLoadingNotifier::NewTabWillLoadUrl(const GURL& url, bool in_incognito) {
   for (auto& observer : observers_)
-    observer.NewTabWillOpenUrl(url, in_incognito);
+    observer.NewTabWillLoadUrl(url, in_incognito);
 }
 
-void UrlLoadingNotifier::NewTabDidOpenUrl(const GURL& url, bool in_incognito) {
+void UrlLoadingNotifier::NewTabDidLoadUrl(const GURL& url, bool in_incognito) {
   for (auto& observer : observers_)
-    observer.NewTabDidOpenUrl(url, in_incognito);
+    observer.NewTabDidLoadUrl(url, in_incognito);
 }
 
 void UrlLoadingNotifier::WillSwitchToTabWithUrl(const GURL& url,
diff --git a/ios/chrome/browser/url_loading/url_loading_observer_bridge.h b/ios/chrome/browser/url_loading/url_loading_observer_bridge.h
index cde6c31..6384921 100644
--- a/ios/chrome/browser/url_loading/url_loading_observer_bridge.h
+++ b/ios/chrome/browser/url_loading/url_loading_observer_bridge.h
@@ -14,18 +14,18 @@
 @protocol URLLoadingObserver <NSObject>
 @optional
 
-// The loader will open |URL| in the current tab. Next state will be
-// one of: tabFailedToOpenURL, tabDidPrerenderURL,
-// tabDidReloadURL or tabDidOpenURL.
-// Invoked by UrlLoadingObserverBridge::TabWillOpenUrl.
-- (void)tabWillOpenURL:(GURL)URL
+// The loader will load |URL| in the current tab. Next state will be
+// one of: tabFailedToLoadURL, tabDidPrerenderURL,
+// tabDidReloadURL or tabDidLoadURL.
+// Invoked by UrlLoadingObserverBridge::TabWillLoadUrl.
+- (void)tabWillLoadURL:(GURL)URL
         transitionType:(ui::PageTransition)transitionType;
 
-// The loader didn't succeed opening the requested |URL|. Reason
+// The loader didn't succeed loading the requested |URL|. Reason
 // can, for example be an incognito mismatch or an induced crash.
-// It is possible that the url was opened, but in another tab.
-// Invoked by UrlLoadingObserverBridge::TabFailedToOpenUrl.
-- (void)tabFailedToOpenURL:(GURL)URL
+// It is possible that the url was loaded, but in another tab.
+// Invoked by UrlLoadingObserverBridge::TabFailedToLoadUrl.
+- (void)tabFailedToLoadURL:(GURL)URL
             transitionType:(ui::PageTransition)transitionType;
 
 // The loader replaced the load with a prerendering.
@@ -39,27 +39,27 @@
          transitionType:(ui::PageTransition)transitionType;
 
 // The loader initiated the |url| loading successfully.
-// Invoked by UrlLoadingObserverBridge::TabDidOpenUrl.
-- (void)tabDidOpenURL:(GURL)URL
+// Invoked by UrlLoadingObserverBridge::TabDidLoadUrl.
+- (void)tabDidLoadURL:(GURL)URL
        transitionType:(ui::PageTransition)transitionType;
 
-// The loader will open |URL| in a new tab. Next state will be:
-// newTabDidOpenURL.
-// Invoked by UrlLoadingObserverBridge::NewTabWillOpenUrl.
-- (void)newTabWillOpenURL:(GURL)URL inIncognito:(BOOL)inIncognito;
+// The loader will load |URL| in a new tab. Next state will be:
+// newTabDidLoadURL.
+// Invoked by UrlLoadingObserverBridge::NewTabWillLoadUrl.
+- (void)newTabWillLoadURL:(GURL)URL inIncognito:(BOOL)inIncognito;
 
 // The loader initiated the |URL| loading in a new tab successfully.
-// Invoked by UrlLoadingObserverBridge::NewTabDidOpenUrl.
-- (void)newTabDidOpenURL:(GURL)URL inIncognito:(BOOL)inIncognito;
+// Invoked by UrlLoadingObserverBridge::NewTabDidLoadUrl.
+- (void)newTabDidLoadURL:(GURL)URL inIncognito:(BOOL)inIncognito;
 
 // The loader will switch to an existing tab with |URL| instead of loading it.
 // Next state will be: didSwitchToTabWithURL. Invoked by
-// UrlLoadingObserverBridge::NewTabWillOpenUrl.
+// UrlLoadingObserverBridge::NewTabWillLoadUrl.
 - (void)willSwitchToTabWithURL:(GURL)URL
               newWebStateIndex:(NSInteger)newWebStateIndex;
 
 // The loader switched to an existing tab with |URL|.
-// Invoked by UrlLoadingObserverBridge::NewTabDidOpenUrl.
+// Invoked by UrlLoadingObserverBridge::NewTabDidLoadUrl.
 - (void)didSwitchToTabWithURL:(GURL)URL
              newWebStateIndex:(NSInteger)newWebStateIndex;
 
@@ -70,14 +70,14 @@
  public:
   UrlLoadingObserverBridge(id<URLLoadingObserver> owner);
 
-  void TabWillOpenUrl(const GURL& url, ui::PageTransition transition_type);
-  void TabFailedToOpenUrl(const GURL& url, ui::PageTransition transition_type);
+  void TabWillLoadUrl(const GURL& url, ui::PageTransition transition_type);
+  void TabFailedToLoadUrl(const GURL& url, ui::PageTransition transition_type);
   void TabDidPrerenderUrl(const GURL& url, ui::PageTransition transition_type);
   void TabDidReloadUrl(const GURL& url, ui::PageTransition transition_type);
-  void TabDidOpenUrl(const GURL& url, ui::PageTransition transition_type);
+  void TabDidLoadUrl(const GURL& url, ui::PageTransition transition_type);
 
-  void NewTabWillOpenUrl(const GURL& url, bool in_incognito);
-  void NewTabDidOpenUrl(const GURL& url, bool in_incognito);
+  void NewTabWillLoadUrl(const GURL& url, bool in_incognito);
+  void NewTabDidLoadUrl(const GURL& url, bool in_incognito);
 
   void WillSwitchToTabWithUrl(const GURL& url, int new_web_state_index);
   void DidSwitchToTabWithUrl(const GURL& url, int new_web_state_index);
diff --git a/ios/chrome/browser/url_loading/url_loading_observer_bridge.mm b/ios/chrome/browser/url_loading/url_loading_observer_bridge.mm
index 2e4c705..575a9df 100644
--- a/ios/chrome/browser/url_loading/url_loading_observer_bridge.mm
+++ b/ios/chrome/browser/url_loading/url_loading_observer_bridge.mm
@@ -11,20 +11,20 @@
 UrlLoadingObserverBridge::UrlLoadingObserverBridge(id<URLLoadingObserver> owner)
     : owner_(owner) {}
 
-void UrlLoadingObserverBridge::TabWillOpenUrl(
+void UrlLoadingObserverBridge::TabWillLoadUrl(
     const GURL& url,
     ui::PageTransition transition_type) {
-  if ([owner_ respondsToSelector:@selector(tabWillOpenURL:transitionType:)]) {
-    [owner_ tabWillOpenURL:url transitionType:transition_type];
+  if ([owner_ respondsToSelector:@selector(tabWillLoadURL:transitionType:)]) {
+    [owner_ tabWillLoadURL:url transitionType:transition_type];
   }
 }
 
-void UrlLoadingObserverBridge::TabFailedToOpenUrl(
+void UrlLoadingObserverBridge::TabFailedToLoadUrl(
     const GURL& url,
     ui::PageTransition transition_type) {
-  if ([owner_ respondsToSelector:@selector(tabFailedToOpenURL:
+  if ([owner_ respondsToSelector:@selector(tabFailedToLoadURL:
                                                transitionType:)]) {
-    [owner_ tabFailedToOpenURL:url transitionType:transition_type];
+    [owner_ tabFailedToLoadURL:url transitionType:transition_type];
   }
 }
 
@@ -45,25 +45,25 @@
   }
 }
 
-void UrlLoadingObserverBridge::TabDidOpenUrl(
+void UrlLoadingObserverBridge::TabDidLoadUrl(
     const GURL& url,
     ui::PageTransition transition_type) {
-  if ([owner_ respondsToSelector:@selector(tabDidOpenURL:transitionType:)]) {
-    [owner_ tabDidOpenURL:url transitionType:transition_type];
+  if ([owner_ respondsToSelector:@selector(tabDidLoadURL:transitionType:)]) {
+    [owner_ tabDidLoadURL:url transitionType:transition_type];
   }
 }
 
-void UrlLoadingObserverBridge::NewTabWillOpenUrl(const GURL& url,
+void UrlLoadingObserverBridge::NewTabWillLoadUrl(const GURL& url,
                                                  bool in_incognito) {
-  if ([owner_ respondsToSelector:@selector(newTabWillOpenURL:inIncognito:)]) {
-    [owner_ newTabWillOpenURL:url inIncognito:in_incognito];
+  if ([owner_ respondsToSelector:@selector(newTabWillLoadURL:inIncognito:)]) {
+    [owner_ newTabWillLoadURL:url inIncognito:in_incognito];
   }
 }
 
-void UrlLoadingObserverBridge::NewTabDidOpenUrl(const GURL& url,
+void UrlLoadingObserverBridge::NewTabDidLoadUrl(const GURL& url,
                                                 bool in_incognito) {
-  if ([owner_ respondsToSelector:@selector(newTabDidOpenURL:inIncognito:)]) {
-    [owner_ newTabDidOpenURL:url inIncognito:in_incognito];
+  if ([owner_ respondsToSelector:@selector(newTabDidLoadURL:inIncognito:)]) {
+    [owner_ newTabDidLoadURL:url inIncognito:in_incognito];
   }
 }
 
diff --git a/ios/chrome/browser/url_loading/url_loading_params.h b/ios/chrome/browser/url_loading/url_loading_params.h
index 95d77ab..70d99d6 100644
--- a/ios/chrome/browser/url_loading/url_loading_params.h
+++ b/ios/chrome/browser/url_loading/url_loading_params.h
@@ -17,7 +17,14 @@
  public:
   // Initializes a UrlLoadParams intended to open in current page.
   static UrlLoadParams* InCurrentTab(
+      const web::NavigationManager::WebLoadParams& web_params,
+      WindowOpenDisposition disposition);
+  // Initializes a UrlLoadParams intended to open in current page, width
+  // disposition set to WindowOpenDisposition::CURRENT_TAB.
+  static UrlLoadParams* InCurrentTab(
       const web::NavigationManager::WebLoadParams& web_params);
+  // Initializes a UrlLoadParams intended to open in current page, width
+  // disposition set to WindowOpenDisposition::CURRENT_TAB.
   static UrlLoadParams* InCurrentTab(const GURL& url);
 
   // Initializes a UrlLoadParams intended to open in a new page.
@@ -28,6 +35,21 @@
                                  bool in_background,
                                  OpenPosition append_to);
 
+  // Initializes a UrlLoadParams intended to open in a new page with virtual_url
+  // set to GURL::EmptyGURL().
+  static UrlLoadParams* InNewTab(const GURL& url,
+                                 const web::Referrer& referrer,
+                                 bool in_incognito,
+                                 bool in_background,
+                                 OpenPosition append_to);
+
+  // Initializes a UrlLoadParams intended to open in a new page with virtual_url
+  // set to GURL::EmptyGURL() and the default web::Referrer().
+  static UrlLoadParams* InNewTab(const GURL& url,
+                                 bool in_incognito,
+                                 bool in_background,
+                                 OpenPosition append_to);
+
   // Initializes a UrlLoadParams intended to open a new page.
   static UrlLoadParams* InNewEmptyTab(bool in_incognito, bool in_background);
 
diff --git a/ios/chrome/browser/url_loading/url_loading_params.mm b/ios/chrome/browser/url_loading/url_loading_params.mm
index 25d9853..756c02d 100644
--- a/ios/chrome/browser/url_loading/url_loading_params.mm
+++ b/ios/chrome/browser/url_loading/url_loading_params.mm
@@ -9,18 +9,21 @@
 #endif
 
 UrlLoadParams* UrlLoadParams::InCurrentTab(
-    const web::NavigationManager::WebLoadParams& web_params) {
+    const web::NavigationManager::WebLoadParams& web_params,
+    WindowOpenDisposition disposition) {
   UrlLoadParams* params = new UrlLoadParams();
-  params->disposition = WindowOpenDisposition::CURRENT_TAB;
+  params->disposition = disposition;
   params->web_params = web_params;
   return params;
 }
 
+UrlLoadParams* UrlLoadParams::InCurrentTab(
+    const web::NavigationManager::WebLoadParams& web_params) {
+  return InCurrentTab(web_params, WindowOpenDisposition::CURRENT_TAB);
+}
+
 UrlLoadParams* UrlLoadParams::InCurrentTab(const GURL& url) {
-  UrlLoadParams* params = new UrlLoadParams();
-  params->disposition = WindowOpenDisposition::CURRENT_TAB;
-  params->web_params = web::NavigationManager::WebLoadParams(url);
-  return params;
+  return InCurrentTab(web::NavigationManager::WebLoadParams(url));
 }
 
 UrlLoadParams* UrlLoadParams::InNewTab(const GURL& url,
@@ -42,6 +45,22 @@
   return params;
 }
 
+UrlLoadParams* UrlLoadParams::InNewTab(const GURL& url,
+                                       const web::Referrer& referrer,
+                                       bool in_incognito,
+                                       bool in_background,
+                                       OpenPosition append_to) {
+  return InNewTab(url, GURL::EmptyGURL(), referrer, in_incognito, in_background,
+                  append_to);
+}
+
+UrlLoadParams* UrlLoadParams::InNewTab(const GURL& url,
+                                       bool in_incognito,
+                                       bool in_background,
+                                       OpenPosition append_to) {
+  return InNewTab(url, web::Referrer(), in_incognito, in_background, append_to);
+}
+
 UrlLoadParams* UrlLoadParams::InNewEmptyTab(bool in_incognito,
                                             bool in_background) {
   UrlLoadParams* params = new UrlLoadParams();
diff --git a/ios/chrome/browser/url_loading/url_loading_service.h b/ios/chrome/browser/url_loading/url_loading_service.h
index 7af35ff..e3b6508 100644
--- a/ios/chrome/browser/url_loading/url_loading_service.h
+++ b/ios/chrome/browser/url_loading/url_loading_service.h
@@ -9,7 +9,6 @@
 #import <UIKit/UIKit.h>
 
 #include "components/keyed_service/core/keyed_service.h"
-#import "ios/chrome/browser/ui/chrome_load_params.h"
 #import "ios/chrome/browser/ui/url_loader.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ui/base/page_transition_types.h"
@@ -41,24 +40,20 @@
   void SetDelegate(id<URLLoadingServiceDelegate> delegate);
   void SetBrowser(Browser* browser);
 
+  // TODO(crbug.com/907527): deprecate this when possible.
   id<UrlLoader> GetUrlLoader();
 
-  // Opens a url based on |chrome_params|.
-  // TODO(crbug.com/907527): to be deprecated, use OpenUrl.
-  void LoadUrlInCurrentTab(const ChromeLoadParams& chrome_params);
-
-  // Opens a url based on |command| in a new tab.
-  // TODO(crbug.com/907527): to be deprecated, use OpenUrl.
-  void LoadUrlInNewTab(OpenNewTabCommand* command);
-
   // Opens a url depending on |params.disposition|.
-  void OpenUrl(UrlLoadParams* params);
+  virtual void Load(UrlLoadParams* params);
 
  private:
-  // Switches to a tab that matches |params.web_params| or opens in a new tab.
+  // Switches to a tab that matches |params.web_params| or loads in a new tab.
   virtual void SwitchToTab(UrlLoadParams* params);
 
+  // Loads a url based on |params| in current tab.
   virtual void LoadUrlInCurrentTab(UrlLoadParams* params);
+
+  // Loads a url based on |params| in a new tab.
   virtual void LoadUrlInNewTab(UrlLoadParams* params);
 
   __weak id<URLLoadingServiceDelegate> delegate_;
diff --git a/ios/chrome/browser/url_loading/url_loading_service.mm b/ios/chrome/browser/url_loading/url_loading_service.mm
index 79238aea..2ad005d 100644
--- a/ios/chrome/browser/url_loading/url_loading_service.mm
+++ b/ios/chrome/browser/url_loading/url_loading_service.mm
@@ -12,6 +12,7 @@
 #import "ios/chrome/browser/prerender/prerender_service_factory.h"
 #import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
+#import "ios/chrome/browser/ui/chrome_load_params.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/ntp/ntp_util.h"
 #import "ios/chrome/browser/url_loading/app_url_loading_service.h"
@@ -77,11 +78,23 @@
 }
 
 - (void)loadURLWithParams:(const ChromeLoadParams&)chromeParams {
-  service_->LoadUrlInCurrentTab(chromeParams);
+  web::NavigationManager::WebLoadParams params = chromeParams.web_params;
+  if (chromeParams.disposition == WindowOpenDisposition::SWITCH_TO_TAB) {
+    service_->Load(UrlLoadParams::SwitchToTab(chromeParams.web_params));
+  } else {
+    service_->Load(UrlLoadParams::InCurrentTab(chromeParams.web_params));
+  }
 }
 
 - (void)webPageOrderedOpen:(OpenNewTabCommand*)command {
-  service_->LoadUrlInNewTab(command);
+  UrlLoadParams* params = UrlLoadParams::InNewTab(
+      command.URL, command.virtualURL, command.referrer, command.inIncognito,
+      command.inBackground, command.appendTo);
+  params->origin_point = command.originPoint;
+  params->from_chrome = command.fromChrome;
+  params->user_initiated = command.userInitiated;
+  params->should_focus_omnibox = command.shouldFocusOmnibox;
+  service_->Load(params);
 }
 
 @end
@@ -101,7 +114,7 @@
   browser_ = browser;
 }
 
-void UrlLoadingService::OpenUrl(UrlLoadParams* params) {
+void UrlLoadingService::Load(UrlLoadParams* params) {
   switch (params->disposition) {
     case WindowOpenDisposition::NEW_BACKGROUND_TAB:
     case WindowOpenDisposition::NEW_FOREGROUND_TAB:
@@ -119,24 +132,12 @@
   }
 }
 
-// TODO(crbug.com/907527): remove this once all calls go through
-// UrlLoadingService::OpenUrl.
-void UrlLoadingService::LoadUrlInCurrentTab(
-    const ChromeLoadParams& chrome_params) {
-  web::NavigationManager::WebLoadParams params = chrome_params.web_params;
-  if (chrome_params.disposition == WindowOpenDisposition::SWITCH_TO_TAB) {
-    SwitchToTab(UrlLoadParams::SwitchToTab(chrome_params.web_params));
-    return;
-  }
-  OpenUrl(UrlLoadParams::InCurrentTab(chrome_params.web_params));
-}
-
 void UrlLoadingService::LoadUrlInCurrentTab(UrlLoadParams* params) {
   web::NavigationManager::WebLoadParams web_params = params->web_params;
 
   ios::ChromeBrowserState* browser_state = browser_->GetBrowserState();
 
-  notifier_->TabWillOpenUrl(web_params.url, web_params.transition_type);
+  notifier_->TabWillLoadUrl(web_params.url, web_params.transition_type);
 
   // NOTE: This check for the Crash Host URL is here to avoid the URL from
   // ending up in the history causing the app to crash at every subsequent
@@ -145,7 +146,7 @@
     InduceBrowserCrash(web_params.url);
     // Under a debugger, the app can continue working even after the CHECK.
     // Adding a return avoids adding the crash url to history.
-    notifier_->TabFailedToOpenUrl(web_params.url, web_params.transition_type);
+    notifier_->TabFailedToLoadUrl(web_params.url, web_params.transition_type);
     return;
   }
 
@@ -167,7 +168,7 @@
   // load a disallowed URL, instead create a new tab not in the incognito state.
   if (browser_state->IsOffTheRecord() &&
       !IsURLAllowedInIncognito(web_params.url)) {
-    notifier_->TabFailedToOpenUrl(web_params.url, web_params.transition_type);
+    notifier_->TabFailedToLoadUrl(web_params.url, web_params.transition_type);
     UrlLoadParams* new_tab_params = UrlLoadParams::InNewTab(
         web_params.url, web_params.virtual_url, web::Referrer(),
         /* in_incognito */ NO,
@@ -201,7 +202,7 @@
 
   current_web_state->GetNavigationManager()->LoadURLWithParams(web_params);
 
-  notifier_->TabDidOpenUrl(web_params.url, web_params.transition_type);
+  notifier_->TabDidLoadUrl(web_params.url, web_params.transition_type);
 }
 
 void UrlLoadingService::SwitchToTab(UrlLoadParams* params) {
@@ -219,10 +220,9 @@
     // If the tab containing the URL has been closed.
     if (old_tab_is_ntp_without_history) {
       // It is NTP, just load the URL.
-      ChromeLoadParams currentPageParams(web_params);
-      LoadUrlInCurrentTab(currentPageParams);
+      Load(UrlLoadParams::InCurrentTab(web_params));
     } else {
-      // Open the URL in foreground.
+      // Load the URL in foreground.
       ios::ChromeBrowserState* browser_state = browser_->GetBrowserState();
       UrlLoadParams* new_tab_params = UrlLoadParams::InNewTab(
           web_params.url, web_params.virtual_url, web::Referrer(),
@@ -248,19 +248,6 @@
   notifier_->DidSwitchToTabWithUrl(web_params.url, new_web_state_index);
 }
 
-// TODO(crbug.com/907527): remove this once all calls go through
-// UrlLoadingService::OpenUrl.
-void UrlLoadingService::LoadUrlInNewTab(OpenNewTabCommand* command) {
-  UrlLoadParams* params = UrlLoadParams::InNewTab(
-      command.URL, command.virtualURL, command.referrer, command.inIncognito,
-      command.inBackground, command.appendTo);
-  params->origin_point = command.originPoint;
-  params->from_chrome = command.fromChrome;
-  params->user_initiated = command.userInitiated;
-  params->should_focus_omnibox = command.shouldFocusOmnibox;
-  OpenUrl(params);
-}
-
 void UrlLoadingService::LoadUrlInNewTab(UrlLoadParams* params) {
   DCHECK(app_service_);
   DCHECK(delegate_);
@@ -268,7 +255,7 @@
   ios::ChromeBrowserState* browser_state = browser_->GetBrowserState();
 
   if (params->in_incognito != browser_state->IsOffTheRecord()) {
-    // When sending an open request that switches modes, ensure the tab
+    // When sending a load request that switches modes, ensure the tab
     // ends up appended to the end of the model, not just next to what is
     // currently selected in the other mode. This is done with the |append_to|
     // parameter.
@@ -280,7 +267,7 @@
   // Notify only after checking incognito match, otherwise the delegate will
   // take of changing the mode and try again. Notifying before the checks can
   // lead to be calling it twice, and calling 'did' below once.
-  notifier_->NewTabWillOpenUrl(params->web_params.url, params->in_incognito);
+  notifier_->NewTabWillLoadUrl(params->web_params.url, params->in_incognito);
 
   TabModel* tab_model = browser_->GetTabModel();
 
@@ -303,7 +290,7 @@
                         atIndex:TabModelConstants::kTabPositionAutomatically
                    inBackground:params->in_background()];
 
-    notifier_->NewTabDidOpenUrl(captured_url, params->in_incognito);
+    notifier_->NewTabDidLoadUrl(captured_url, params->in_incognito);
   };
 
   if (!params->in_background()) {
diff --git a/ios/chrome/browser/url_loading/url_loading_service_unittest.mm b/ios/chrome/browser/url_loading/url_loading_service_unittest.mm
index ff6d6e45..a3d8b724 100644
--- a/ios/chrome/browser/url_loading/url_loading_service_unittest.mm
+++ b/ios/chrome/browser/url_loading/url_loading_service_unittest.mm
@@ -211,7 +211,7 @@
 
   ASSERT_EQ(web_state_ptr, web_state_list->GetActiveWebState());
 
-  service_->OpenUrl(
+  service_->Load(
       UrlLoadParams::SwitchToTab(web::NavigationManager::WebLoadParams(url)));
   EXPECT_EQ(web_state_ptr_2, web_state_list->GetActiveWebState());
   EXPECT_EQ(0, app_service_->load_new_tab_call_count);
@@ -242,7 +242,7 @@
 
   ASSERT_EQ(web_state_ptr, web_state_list->GetActiveWebState());
 
-  service_->OpenUrl(
+  service_->Load(
       UrlLoadParams::SwitchToTab(web::NavigationManager::WebLoadParams(url)));
   EXPECT_EQ(web_state_ptr_2, web_state_list->GetActiveWebState());
   EXPECT_EQ(1, web_state_list->count());
@@ -265,7 +265,7 @@
 
   GURL url("http://test/2");
 
-  service_->OpenUrl(
+  service_->Load(
       UrlLoadParams::SwitchToTab(web::NavigationManager::WebLoadParams(url)));
   EXPECT_EQ(1, web_state_list->count());
   EXPECT_EQ(web_state_ptr, web_state_list->GetActiveWebState());
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 3e5db15..518d087 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -483,6 +483,7 @@
     "//components/url_formatter",
     "//ios/net",
     "//ios/testing:ocmock_support",
+    "//ios/web/common",
     "//ios/web/navigation",
     "//ios/web/navigation:core",
     "//ios/web/public",
diff --git a/ios/web/common/BUILD.gn b/ios/web/common/BUILD.gn
new file mode 100644
index 0000000..0b7c9014
--- /dev/null
+++ b/ios/web/common/BUILD.gn
@@ -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.
+
+import("//ios/build/config.gni")
+
+source_set("common") {
+  sources = [
+    "crw_content_view.h",
+  ]
+
+  libs = [ "UIKit.framework" ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/web/common/DEPS b/ios/web/common/DEPS
new file mode 100644
index 0000000..f07d4508
--- /dev/null
+++ b/ios/web/common/DEPS
@@ -0,0 +1,12 @@
+include_rules = [
+  # common interfaces cannot depend on private web code.
+  "-ios/web",
+  "+ios/web/common",
+]
+
+specific_include_rules = {
+  # Implementations of common interfaces can depend on private web code.
+  "^.*\.(cc|mm)$": [
+    "+ios/web",
+  ],
+}
diff --git a/ios/web/common/README.md b/ios/web/common/README.md
new file mode 100644
index 0000000..50b0f8c
--- /dev/null
+++ b/ios/web/common/README.md
@@ -0,0 +1 @@
+This directory contains common code shared between //ios/web and the rest of the layers (//components, //ios/chrome, //ios/web_view). This directory does not contain the public API to create and manage web contents (**//ios/web//public** is public API directory).
diff --git a/ios/web/public/web_state/ui/crw_content_view.h b/ios/web/common/crw_content_view.h
similarity index 86%
rename from ios/web/public/web_state/ui/crw_content_view.h
rename to ios/web/common/crw_content_view.h
index ed38357..69a90a7 100644
--- a/ios/web/public/web_state/ui/crw_content_view.h
+++ b/ios/web/common/crw_content_view.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_CONTENT_VIEW_H_
-#define IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_CONTENT_VIEW_H_
+#ifndef IOS_WEB_COMMON_CRW_CONTENT_VIEW_H_
+#define IOS_WEB_COMMON_CRW_CONTENT_VIEW_H_
 
 #import <UIKit/UIKit.h>
 
 // UIViews conforming to CRWScrollableContent (i.e. CRWContentViews) are used
 // to display content within a WebState.
-@protocol CRWScrollableContent<NSObject>
+@protocol CRWScrollableContent <NSObject>
 
 // The scroll view used to display the content.  If |scrollView| is non-nil,
 // it will be used to back the CRWContentViewScrollViewProxy and is expected to
@@ -38,4 +38,4 @@
 // Convenience type for content views.
 typedef UIView<CRWScrollableContent> CRWContentView;
 
-#endif  // IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_CONTENT_VIEW_H_
+#endif  // IOS_WEB_COMMON_CRW_CONTENT_VIEW_H_
diff --git a/ios/web/interstitials/BUILD.gn b/ios/web/interstitials/BUILD.gn
index 2e38042..684ad44 100644
--- a/ios/web/interstitials/BUILD.gn
+++ b/ios/web/interstitials/BUILD.gn
@@ -7,6 +7,7 @@
 source_set("interstitials") {
   deps = [
     "//base",
+    "//ios/web/common",
     "//ios/web/navigation:core",
     "//ios/web/public",
     "//ios/web/web_state:web_state_impl_header",
diff --git a/ios/web/interstitials/web_interstitial_impl.h b/ios/web/interstitials/web_interstitial_impl.h
index ec8959aa..96aa7dc 100644
--- a/ios/web/interstitials/web_interstitial_impl.h
+++ b/ios/web/interstitials/web_interstitial_impl.h
@@ -7,8 +7,8 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/web/common/crw_content_view.h"
 #include "ios/web/public/interstitials/web_interstitial.h"
-#import "ios/web/public/web_state/ui/crw_content_view.h"
 #include "ios/web/public/web_state/web_state_observer.h"
 #import "ios/web/web_state/ui/web_view_js_utils.h"
 #include "url/gurl.h"
diff --git a/ios/web/public/BUILD.gn b/ios/web/public/BUILD.gn
index 1c60fae..e877913 100644
--- a/ios/web/public/BUILD.gn
+++ b/ios/web/public/BUILD.gn
@@ -13,6 +13,7 @@
   ]
 
   deps = [
+    "//ios/web/common",
     "//services/service_manager/public/cpp",
     "//ui/base",
   ]
@@ -70,7 +71,6 @@
     "web_state/page_display_state.h",
     "web_state/page_display_state.mm",
     "web_state/session_certificate_policy_cache.h",
-    "web_state/ui/crw_content_view.h",
     "web_state/ui/crw_context_menu_delegate.h",
     "web_state/ui/crw_native_content.h",
     "web_state/ui/crw_native_content_provider.h",
diff --git a/ios/web/public/DEPS b/ios/web/public/DEPS
index 95ec6dc..477ed8a 100644
--- a/ios/web/public/DEPS
+++ b/ios/web/public/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   # web interfaces cannot depend on private web code.
   "-ios/web",
+  "+ios/web/common",
   "+ios/web/public",
   "+services/network/public/mojom",
 ]
diff --git a/ios/web/public/test/fakes/BUILD.gn b/ios/web/public/test/fakes/BUILD.gn
index 0bb0e831..03fc2410 100644
--- a/ios/web/public/test/fakes/BUILD.gn
+++ b/ios/web/public/test/fakes/BUILD.gn
@@ -8,6 +8,7 @@
 
   deps = [
     "//base",
+    "//ios/web/common",
     "//ios/web/public/download",
     "//ios/web/public/find_in_page",
     "//ios/web/test:test_constants",
diff --git a/ios/web/public/test/fakes/test_web_state.mm b/ios/web/public/test/fakes/test_web_state.mm
index 6d00300..5b773a7 100644
--- a/ios/web/public/test/fakes/test_web_state.mm
+++ b/ios/web/public/test/fakes/test_web_state.mm
@@ -11,10 +11,10 @@
 #include "base/callback.h"
 #import "base/strings/sys_string_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#import "ios/web/common/crw_content_view.h"
 #import "ios/web/public/crw_navigation_item_storage.h"
 #import "ios/web/public/crw_session_storage.h"
 #import "ios/web/public/serializable_user_data_manager.h"
-#import "ios/web/public/web_state/ui/crw_content_view.h"
 #include "ios/web/public/web_state/web_frame.h"
 #import "ios/web/public/web_state/web_state_policy_decider.h"
 #include "ios/web/web_state/web_frames_manager_impl.h"
diff --git a/ios/web/public/web_state/ui/crw_web_view_content_view.h b/ios/web/public/web_state/ui/crw_web_view_content_view.h
index f6ea2662..4038def2 100644
--- a/ios/web/public/web_state/ui/crw_web_view_content_view.h
+++ b/ios/web/public/web_state/ui/crw_web_view_content_view.h
@@ -5,7 +5,7 @@
 #ifndef IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_WEB_VIEW_CONTENT_VIEW_H_
 #define IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_WEB_VIEW_CONTENT_VIEW_H_
 
-#import "ios/web/public/web_state/ui/crw_content_view.h"
+#import "ios/web/common/crw_content_view.h"
 
 // Wraps a web vew in a CRWContentView.
 @interface CRWWebViewContentView : CRWContentView
diff --git a/ios/web/public/webui/web_ui_ios_controller_factory.h b/ios/web/public/webui/web_ui_ios_controller_factory.h
index 01e076af..c224dee 100644
--- a/ios/web/public/webui/web_ui_ios_controller_factory.h
+++ b/ios/web/public/webui/web_ui_ios_controller_factory.h
@@ -24,6 +24,9 @@
   // Call to register a factory.
   static void RegisterFactory(WebUIIOSControllerFactory* factory);
 
+  // Returns whether |url| has an associated WebUI URL controller.
+  virtual bool HasWebUIIOSControllerForURL(const GURL& url) const = 0;
+
   // Returns a WebUIIOSController instance for the given URL, or NULL if the URL
   // doesn't correspond to a WebUIIOS.
   virtual std::unique_ptr<WebUIIOSController> CreateWebUIIOSControllerForURL(
diff --git a/ios/web/web_state/BUILD.gn b/ios/web/web_state/BUILD.gn
index 2ce3920..b5d82d3 100644
--- a/ios/web/web_state/BUILD.gn
+++ b/ios/web/web_state/BUILD.gn
@@ -12,6 +12,7 @@
     ":web_state_impl_header",
     ":wk_web_view_security_util",
     "//base",
+    "//ios/web/common",
     "//ios/web/interstitials",
     "//ios/web/navigation",
     "//ios/web/navigation:core",
diff --git a/ios/web/web_state/ui/BUILD.gn b/ios/web/web_state/ui/BUILD.gn
index 2657aa7..b313788 100644
--- a/ios/web/web_state/ui/BUILD.gn
+++ b/ios/web/web_state/ui/BUILD.gn
@@ -23,6 +23,7 @@
     "//ios/net",
     "//ios/web:core",
     "//ios/web/browsing_data",
+    "//ios/web/common",
     "//ios/web/find_in_page",
     "//ios/web/interstitials",
     "//ios/web/navigation",
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 837cad0..39b2a6b9 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -41,6 +41,7 @@
 #include "base/values.h"
 #include "crypto/symmetric_key.h"
 #import "ios/net/http_response_headers_util.h"
+#import "ios/web/common/crw_content_view.h"
 #import "ios/web/find_in_page/find_in_page_manager_impl.h"
 #include "ios/web/history_state_util.h"
 #import "ios/web/interstitials/web_interstitial_impl.h"
@@ -70,7 +71,6 @@
 #import "ios/web/public/web_state/js/crw_js_injection_manager.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
 #import "ios/web/public/web_state/page_display_state.h"
-#import "ios/web/public/web_state/ui/crw_content_view.h"
 #import "ios/web/public/web_state/ui/crw_context_menu_delegate.h"
 #import "ios/web/public/web_state/ui/crw_native_content.h"
 #import "ios/web/public/web_state/ui/crw_native_content_provider.h"
@@ -3463,8 +3463,9 @@
   // |CreateWebUI| will do nothing if |URL| is not a WebUI URL and then
   // |HasWebUI| will return false.
   self.webStateImpl->CreateWebUI(URL);
-  bool isWebUIURL = self.webStateImpl->HasWebUI();
-  if (isWebUIURL && !web::features::WebUISchemeHandlingEnabled()) {
+  bool HasWebUIIOSControllerForURL = self.webStateImpl->HasWebUI();
+  if (HasWebUIIOSControllerForURL &&
+      !web::features::WebUISchemeHandlingEnabled()) {
     _webUIManager =
         [[CRWWebUIManager alloc] initWithWebState:self.webStateImpl];
   }
diff --git a/ios/web/web_state/ui/crw_web_controller_container_view.h b/ios/web/web_state/ui/crw_web_controller_container_view.h
index 5b6d44e3..201a8a7 100644
--- a/ios/web/web_state/ui/crw_web_controller_container_view.h
+++ b/ios/web/web_state/ui/crw_web_controller_container_view.h
@@ -7,7 +7,7 @@
 
 #import <UIKit/UIKit.h>
 
-#import "ios/web/public/web_state/ui/crw_content_view.h"
+#import "ios/web/common/crw_content_view.h"
 
 @class CRWWebControllerContainerView;
 @class CRWWebViewContentView;
diff --git a/ios/web/web_state/ui/crw_web_controller_container_view.mm b/ios/web/web_state/ui/crw_web_controller_container_view.mm
index e2af8c2..dae891b 100644
--- a/ios/web/web_state/ui/crw_web_controller_container_view.mm
+++ b/ios/web/web_state/ui/crw_web_controller_container_view.mm
@@ -5,7 +5,7 @@
 #import "ios/web/web_state/ui/crw_web_controller_container_view.h"
 
 #include "base/logging.h"
-#import "ios/web/public/web_state/ui/crw_content_view.h"
+#import "ios/web/common/crw_content_view.h"
 #import "ios/web/public/web_state/ui/crw_native_content.h"
 #import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
 #import "ios/web/web_state/ui/crw_web_view_proxy_impl.h"
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index 70f41df..524dfb8 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -16,6 +16,7 @@
 #import "base/test/ios/wait_util.h"
 #include "base/test/scoped_feature_list.h"
 #import "ios/testing/ocmock_complex_type_helper.h"
+#import "ios/web/common/crw_content_view.h"
 #import "ios/web/navigation/crw_session_controller.h"
 #import "ios/web/navigation/navigation_item_impl.h"
 #import "ios/web/navigation/navigation_manager_impl.h"
@@ -35,7 +36,6 @@
 #include "ios/web/public/test/fakes/test_web_state_observer.h"
 #import "ios/web/public/test/fakes/test_web_view_content_view.h"
 #import "ios/web/public/test/web_view_content_test_util.h"
-#import "ios/web/public/web_state/ui/crw_content_view.h"
 #import "ios/web/public/web_state/ui/crw_native_content.h"
 #import "ios/web/public/web_state/ui/crw_native_content_provider.h"
 #import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
diff --git a/ios/web/web_state/ui/crw_web_view_proxy_impl.h b/ios/web/web_state/ui/crw_web_view_proxy_impl.h
index e2bfd5a..fb4323a0 100644
--- a/ios/web/web_state/ui/crw_web_view_proxy_impl.h
+++ b/ios/web/web_state/ui/crw_web_view_proxy_impl.h
@@ -7,7 +7,7 @@
 
 #import <UIKit/UIKit.h>
 
-#import "ios/web/public/web_state/ui/crw_content_view.h"
+#import "ios/web/common/crw_content_view.h"
 #import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
 
 @class CRWWebController;
diff --git a/ios/web/web_state/ui/crw_web_view_proxy_impl.mm b/ios/web/web_state/ui/crw_web_view_proxy_impl.mm
index 7ba42f5..5e55401 100644
--- a/ios/web/web_state/ui/crw_web_view_proxy_impl.mm
+++ b/ios/web/web_state/ui/crw_web_view_proxy_impl.mm
@@ -4,7 +4,7 @@
 
 #import "ios/web/web_state/ui/crw_web_view_proxy_impl.h"
 
-#import "ios/web/public/web_state/ui/crw_content_view.h"
+#import "ios/web/common/crw_content_view.h"
 #import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index 11754b2..df7eafb 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -13,6 +13,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#import "ios/web/common/crw_content_view.h"
 #import "ios/web/interstitials/web_interstitial_impl.h"
 #import "ios/web/navigation/crw_session_controller.h"
 #import "ios/web/navigation/legacy_navigation_manager_impl.h"
@@ -29,7 +30,6 @@
 #include "ios/web/public/url_util.h"
 #import "ios/web/public/web_client.h"
 #import "ios/web/public/web_state/context_menu_params.h"
-#import "ios/web/public/web_state/ui/crw_content_view.h"
 #import "ios/web/public/web_state/ui/crw_native_content.h"
 #import "ios/web/public/web_state/web_state_delegate.h"
 #include "ios/web/public/web_state/web_state_interface_provider.h"
diff --git a/ios/web/web_state/web_state_unittest.mm b/ios/web/web_state/web_state_unittest.mm
index 4196fee..d01dec1f 100644
--- a/ios/web/web_state/web_state_unittest.mm
+++ b/ios/web/web_state/web_state_unittest.mm
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/path_service.h"
+#include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -83,7 +84,8 @@
 
 // Test fixture for web::WebTest class.
 class WebStateTest
-    : public WebTestWithWebState,
+    : public TestWebClient,
+      public WebTestWithWebState,
       public ::testing::WithParamInterface<NavigationManagerChoice> {
  protected:
   WebStateTest() {
@@ -646,8 +648,13 @@
     return !web_state()->IsLoading();
   }));
   // Wait for the error loading.
-  EXPECT_TRUE(
-      test::WaitForWebViewContainingText(web_state(), "unsupported URL"));
+  std::string error;
+  if (features::WebUISchemeHandlingEnabled()) {
+    error = "NSURLErrorDomain error -1000.";
+  } else {
+    error = "unsupported URL";
+  }
+  EXPECT_TRUE(test::WaitForWebViewContainingText(web_state(), error));
   NSString* data_html = @(kTestPageHTML);
   web_state()->LoadData([data_html dataUsingEncoding:NSUTF8StringEncoding],
                         @"text/html", GURL("https://www.chromium.org"));
@@ -670,8 +677,13 @@
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{
     return !web_state()->IsLoading();
   }));
-  EXPECT_TRUE(
-      test::WaitForWebViewContainingText(web_state(), "unsupported URL"));
+  std::string error;
+  if (features::WebUISchemeHandlingEnabled()) {
+    error = "NSURLErrorDomain error -1000.";
+  } else {
+    error = "unsupported URL";
+  }
+  EXPECT_TRUE(test::WaitForWebViewContainingText(web_state(), error));
   NSString* data_html = @(kTestPageHTML);
   web_state()->LoadData([data_html dataUsingEncoding:NSUTF8StringEncoding],
                         @"text/html", echo_url);
diff --git a/ios/web/webui/crw_web_ui_scheme_handler.mm b/ios/web/webui/crw_web_ui_scheme_handler.mm
index 0fae559..5fe0218 100644
--- a/ios/web/webui/crw_web_ui_scheme_handler.mm
+++ b/ios/web/webui/crw_web_ui_scheme_handler.mm
@@ -7,6 +7,7 @@
 #include <map>
 
 #import "ios/web/webui/url_fetcher_block_adapter.h"
+#include "ios/web/webui/web_ui_ios_controller_factory_registry.h"
 #import "net/base/mac/url_conversions.h"
 #include "url/gurl.h"
 
@@ -36,6 +37,24 @@
     startURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask
     API_AVAILABLE(ios(11.0)) {
   GURL URL = net::GURLWithNSURL(urlSchemeTask.request.URL);
+  web::WebUIIOSControllerFactory* factory =
+      web::WebUIIOSControllerFactoryRegistry::GetInstance();
+  // Check the mainDocumentURL as the URL might be one of the subresource, so
+  // not a WebUI URL itself.
+  if (!factory || !factory->HasWebUIIOSControllerForURL(net::GURLWithNSURL(
+                      urlSchemeTask.request.mainDocumentURL))) {
+    NSError* error =
+        [NSError errorWithDomain:NSURLErrorDomain
+                            code:NSURLErrorUnsupportedURL
+                        userInfo:@{
+                          NSURLErrorKey : urlSchemeTask.request.URL,
+                          NSURLErrorFailingURLStringErrorKey :
+                              urlSchemeTask.request.URL.absoluteString
+                        }];
+    [urlSchemeTask didFailWithError:error];
+    return;
+  }
+
   __weak CRWWebUISchemeHandler* weakSelf = self;
   std::unique_ptr<web::URLFetcherBlockAdapter> adapter =
       std::make_unique<web::URLFetcherBlockAdapter>(
diff --git a/ios/web/webui/crw_web_ui_scheme_handler_unittest.mm b/ios/web/webui/crw_web_ui_scheme_handler_unittest.mm
index 70027392..fee1185 100644
--- a/ios/web/webui/crw_web_ui_scheme_handler_unittest.mm
+++ b/ios/web/webui/crw_web_ui_scheme_handler_unittest.mm
@@ -5,8 +5,15 @@
 #import "ios/web/webui/crw_web_ui_scheme_handler.h"
 
 #include "base/run_loop.h"
+#include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #include "ios/web/public/test/web_test.h"
+#include "ios/web/public/webui/web_ui_ios_controller.h"
+#include "ios/web/public/webui/web_ui_ios_controller_factory.h"
+#include "ios/web/test/test_url_constants.h"
+#import "net/base/mac/url_conversions.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -14,7 +21,11 @@
 
 @interface FakeSchemeTask : NSObject <WKURLSchemeTask>
 
+// Override from the protocol to have it readwrite.
+@property(nonatomic, readwrite, copy) NSURLRequest* request;
+
 @property(nonatomic, assign) BOOL receivedData;
+@property(nonatomic, assign) BOOL receivedError;
 
 @end
 
@@ -33,58 +44,137 @@
 }
 
 - (void)didFailWithError:(NSError*)error {
+  self.receivedError = YES;
 }
 
 @end
 
 namespace web {
 
+namespace {
+class FakeWebUIIOSControllerFactory : public WebUIIOSControllerFactory {
+  bool HasWebUIIOSControllerForURL(const GURL& url) const override {
+    return url.SchemeIs(kTestWebUIScheme);
+  }
+
+  std::unique_ptr<WebUIIOSController> CreateWebUIIOSControllerForURL(
+      WebUIIOS* web_ui,
+      const GURL& url) const override {
+    return nullptr;
+  }
+};
+}  // namespace
+
 // Test fixture for testing CRWWebUISchemeManager.
 class CRWWebUISchemeManagerTest : public WebTest {
+ public:
+  CRWWebUISchemeManagerTest()
+      : test_shared_loader_factory_(
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_)) {
+    WebUIIOSControllerFactory::RegisterFactory(&factory_);
+  }
+
  protected:
   CRWWebUISchemeHandler* CreateSchemeHandler() API_AVAILABLE(ios(11.0)) {
     return [[CRWWebUISchemeHandler alloc]
-        initWithURLLoaderFactory:GetBrowserState()
-                                     ->GetSharedURLLoaderFactory()];
+        initWithURLLoaderFactory:GetSharedURLLoaderFactory()];
   }
+
+  NSURL* GetWebUIURL() {
+    NSString* url = [base::SysUTF8ToNSString(kTestWebUIScheme)
+        stringByAppendingString:@"://testpage"];
+    return [NSURL URLWithString:url];
+  }
+
+  scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory() {
+    return test_shared_loader_factory_;
+  }
+
+  network::TestURLLoaderFactory* GetURLLoaderFactory() {
+    return &test_url_loader_factory_;
+  }
+
+  void RespondWithData(const GURL& url, const std::string& data) {
+    GetURLLoaderFactory()->AddResponse(url.spec(), data);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+  FakeWebUIIOSControllerFactory factory_;
 };
 
-// Tests that calling start on the scheme handler returns some data.
-TEST_F(CRWWebUISchemeManagerTest, StartTask) {
-  if (@available(iOS 11, *)) {
-    CRWWebUISchemeHandler* scheme_handler = CreateSchemeHandler();
-    WKWebView* web_view;
-    FakeSchemeTask* url_scheme_task = [[FakeSchemeTask alloc] init];
-    [scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
+// Tests that calling start on the scheme handler returns some data when the URL
+// is a WebUI URL.
+TEST_F(CRWWebUISchemeManagerTest, StartTaskWithCorrectURL) {
+  CRWWebUISchemeHandler* scheme_handler = CreateSchemeHandler();
+  WKWebView* web_view;
+  FakeSchemeTask* url_scheme_task = [[FakeSchemeTask alloc] init];
+  NSMutableURLRequest* request =
+      [NSMutableURLRequest requestWithURL:GetWebUIURL()];
+  request.mainDocumentURL = GetWebUIURL();
+  url_scheme_task.request = request;
 
-    // Run the runloop to let the url loader send back an answer.
-    EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
-        base::test::ios::kWaitForPageLoadTimeout, ^bool {
-          base::RunLoop().RunUntilIdle();
-          // May or may not require |base::RunLoop().RunUntilIdle();| call.
-          return url_scheme_task.receivedData;
-        }));
-  }
+  [scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
+
+  RespondWithData(net::GURLWithNSURL(request.URL), "{}");
+  EXPECT_TRUE(url_scheme_task.receivedData);
+  EXPECT_FALSE(url_scheme_task.receivedError);
+}
+
+// Tests that calling start on the scheme handler returns some data when the URL
+// is *not* a WebUI URL but the main document URL is.
+TEST_F(CRWWebUISchemeManagerTest, StartTaskWithCorrectMainURL) {
+  CRWWebUISchemeHandler* scheme_handler = CreateSchemeHandler();
+  WKWebView* web_view;
+  FakeSchemeTask* url_scheme_task = [[FakeSchemeTask alloc] init];
+  NSMutableURLRequest* request = [NSMutableURLRequest
+      requestWithURL:[NSURL URLWithString:@"https://notAWebUIURL"]];
+  request.mainDocumentURL = GetWebUIURL();
+  url_scheme_task.request = request;
+
+  [scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
+
+  RespondWithData(net::GURLWithNSURL(request.URL), "{}");
+  EXPECT_TRUE(url_scheme_task.receivedData);
+  EXPECT_FALSE(url_scheme_task.receivedError);
+}
+
+// Tests that calling start on the scheme handler returns an error when the URL
+// is correct but the mainDocumentURL is wrong.
+TEST_F(CRWWebUISchemeManagerTest, StartTaskWithWrongMainDocumentURL) {
+  CRWWebUISchemeHandler* scheme_handler = CreateSchemeHandler();
+  WKWebView* web_view;
+  FakeSchemeTask* url_scheme_task = [[FakeSchemeTask alloc] init];
+  NSMutableURLRequest* request =
+      [NSMutableURLRequest requestWithURL:GetWebUIURL()];
+  request.mainDocumentURL = [NSURL URLWithString:@"https://notAWebUIURL"];
+  url_scheme_task.request = request;
+
+  [scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
+
+  RespondWithData(net::GURLWithNSURL(request.URL), "{}");
+  EXPECT_FALSE(url_scheme_task.receivedData);
+  EXPECT_TRUE(url_scheme_task.receivedError);
 }
 
 // Tests that calling stop right after start prevent the handler from returning
 // data.
 TEST_F(CRWWebUISchemeManagerTest, StopTask) {
-  if (@available(iOS 11, *)) {
-    CRWWebUISchemeHandler* scheme_handler = CreateSchemeHandler();
-    WKWebView* web_view;
-    FakeSchemeTask* url_scheme_task = [[FakeSchemeTask alloc] init];
+  CRWWebUISchemeHandler* scheme_handler = CreateSchemeHandler();
+  WKWebView* web_view;
+  FakeSchemeTask* url_scheme_task = [[FakeSchemeTask alloc] init];
+  NSMutableURLRequest* request =
+      [NSMutableURLRequest requestWithURL:GetWebUIURL()];
+  request.mainDocumentURL = GetWebUIURL();
+  url_scheme_task.request = request;
 
-    [scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
-    [scheme_handler webView:web_view stopURLSchemeTask:url_scheme_task];
+  [scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
+  [scheme_handler webView:web_view stopURLSchemeTask:url_scheme_task];
 
-    // Run the runloop to let the url loader send back an answer.
-    EXPECT_FALSE(base::test::ios::WaitUntilConditionOrTimeout(
-        base::test::ios::kWaitForPageLoadTimeout, ^bool {
-          base::RunLoop().RunUntilIdle();
-          // May or may not require |base::RunLoop().RunUntilIdle();| call.
-          return url_scheme_task.receivedData;
-        }));
-  }
+  RespondWithData(net::GURLWithNSURL(request.URL), "{}");
+  EXPECT_FALSE(url_scheme_task.receivedData);
+  EXPECT_FALSE(url_scheme_task.receivedError);
 }
 }  // namespace web
diff --git a/ios/web/webui/web_ui_ios_controller_factory_registry.cc b/ios/web/webui/web_ui_ios_controller_factory_registry.cc
index c06a51d..fbbc1199 100644
--- a/ios/web/webui/web_ui_ios_controller_factory_registry.cc
+++ b/ios/web/webui/web_ui_ios_controller_factory_registry.cc
@@ -32,6 +32,15 @@
   return instance.get();
 }
 
+bool WebUIIOSControllerFactoryRegistry::HasWebUIIOSControllerForURL(
+    const GURL& url) const {
+  for (WebUIIOSControllerFactory* factory : GetGlobalFactories()) {
+    if (factory->HasWebUIIOSControllerForURL(url))
+      return true;
+  }
+  return false;
+}
+
 std::unique_ptr<WebUIIOSController>
 WebUIIOSControllerFactoryRegistry::CreateWebUIIOSControllerForURL(
     WebUIIOS* web_ui,
diff --git a/ios/web/webui/web_ui_ios_controller_factory_registry.h b/ios/web/webui/web_ui_ios_controller_factory_registry.h
index eba13ec..56c1cb8c 100644
--- a/ios/web/webui/web_ui_ios_controller_factory_registry.h
+++ b/ios/web/webui/web_ui_ios_controller_factory_registry.h
@@ -19,6 +19,8 @@
  public:
   static WebUIIOSControllerFactoryRegistry* GetInstance();
 
+  bool HasWebUIIOSControllerForURL(const GURL& url) const override;
+
   // WebUIIOSControllerFactory implementation. Each method loops through the
   // same method on all the factories.
   std::unique_ptr<WebUIIOSController> CreateWebUIIOSControllerForURL(
diff --git a/ios/web/webui/web_ui_mojo_inttest.mm b/ios/web/webui/web_ui_mojo_inttest.mm
index 08e4c57..cca60245 100644
--- a/ios/web/webui/web_ui_mojo_inttest.mm
+++ b/ios/web/webui/web_ui_mojo_inttest.mm
@@ -131,6 +131,10 @@
     return std::make_unique<TestUI>(web_ui, ui_handler_);
   }
 
+  bool HasWebUIIOSControllerForURL(const GURL& url) const override {
+    return url.SchemeIs(kTestWebUIScheme);
+  }
+
  private:
   // UI handler class which communicates with test WebUI page.
   TestUIHandler* ui_handler_;
diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc
index ec04c77..df6ec39 100644
--- a/ipc/ipc_message_utils.cc
+++ b/ipc/ipc_message_utils.cc
@@ -92,7 +92,7 @@
 
   switch (value->type()) {
     case base::Value::Type::NONE:
-    break;
+      break;
     case base::Value::Type::BOOLEAN: {
       bool val;
       result = value->GetAsBoolean(&val);
@@ -147,6 +147,11 @@
       }
       break;
     }
+
+    // TODO(crbug.com/859477): Remove after root cause is found.
+    default:
+      CHECK(false);
+      break;
   }
 }
 
@@ -260,7 +265,9 @@
       break;
     }
     default:
-    return false;
+      // TODO(crbug.com/859477): Remove after root cause is found.
+      CHECK(false);
+      return false;
   }
 
   return true;
diff --git a/media/capture/video/chromeos/local_gpu_memory_buffer_manager.cc b/media/capture/video/chromeos/local_gpu_memory_buffer_manager.cc
index acc18fd..bf319f4f 100644
--- a/media/capture/video/chromeos/local_gpu_memory_buffer_manager.cc
+++ b/media/capture/video/chromeos/local_gpu_memory_buffer_manager.cc
@@ -71,12 +71,12 @@
     handle_.type = gfx::NATIVE_PIXMAP;
     // Set a dummy id since this is for testing only.
     handle_.id = gfx::GpuMemoryBufferId(0);
-    for (size_t i = 0; i < gbm_bo_get_num_planes(buffer_object); ++i) {
+    for (size_t i = 0; i < gbm_bo_get_plane_count(buffer_object); ++i) {
       handle_.native_pixmap_handle.fds.push_back(
           base::FileDescriptor(gbm_bo_get_plane_fd(buffer_object, i), true));
       handle_.native_pixmap_handle.planes.push_back(
-          gfx::NativePixmapPlane(gbm_bo_get_plane_stride(buffer_object, i),
-                                 gbm_bo_get_plane_offset(buffer_object, i),
+          gfx::NativePixmapPlane(gbm_bo_get_stride_for_plane(buffer_object, i),
+                                 gbm_bo_get_offset(buffer_object, i),
                                  gbm_bo_get_plane_size(buffer_object, i)));
     }
   }
@@ -98,7 +98,7 @@
     if (mapped_) {
       return true;
     }
-    size_t num_planes = gbm_bo_get_num_planes(buffer_object_);
+    size_t num_planes = gbm_bo_get_plane_count(buffer_object_);
     uint32_t stride;
     mapped_planes_.resize(num_planes);
     for (size_t i = 0; i < num_planes; ++i) {
@@ -151,7 +151,7 @@
   gfx::BufferFormat GetFormat() const override { return format_; }
 
   int stride(size_t plane) const override {
-    return gbm_bo_get_plane_stride(buffer_object_, plane);
+    return gbm_bo_get_stride_for_plane(buffer_object_, plane);
   }
 
   void SetColorSpace(const gfx::ColorSpace& color_space) override {}
diff --git a/media/filters/BUILD.gn b/media/filters/BUILD.gn
index 7062ae8c..81c1e4fc 100644
--- a/media/filters/BUILD.gn
+++ b/media/filters/BUILD.gn
@@ -218,6 +218,17 @@
       "h264_bitstream_buffer.h",
     ]
   }
+
+  if (is_fuchsia) {
+    sources += [
+      "fuchsia/fuchsia_video_decoder.cc",
+      "fuchsia/fuchsia_video_decoder.h",
+    ]
+    deps += [
+      "//third_party/fuchsia-sdk/sdk:media",
+      "//third_party/fuchsia-sdk/sdk:mediacodec",
+    ]
+  }
 }
 
 # This component allows other targets to use the JPEG parser as a standalone,
@@ -345,6 +356,10 @@
     deps += [ "//ui/gl" ]
   }
 
+  if (is_fuchsia) {
+    sources += [ "fuchsia/fuchsia_video_decoder_unittest.cc" ]
+  }
+
   # libvpx for running vpx test on chromecast doesn't support high bit depth.
   # This may cause some unit tests failure.
   if (is_chromecast) {
diff --git a/media/filters/decoder_stream.cc b/media/filters/decoder_stream.cc
index 5001c13..325a8b6 100644
--- a/media/filters/decoder_stream.cc
+++ b/media/filters/decoder_stream.cc
@@ -958,14 +958,15 @@
   if (ready_outputs_.size() >= static_cast<size_t>(GetMaxDecodeRequests()))
     return;
 
-  TRACE_EVENT_ASYNC_BEGIN1(
-      "media", GetPrepareTraceString<StreamType>(), this, "timestamp_us",
-      unprepared_outputs_.front()->timestamp().InMicroseconds());
+  // Retain a copy to avoid dangling reference in OnPreparedOutputReady().
+  const scoped_refptr<Output> output = unprepared_outputs_.front();
+  TRACE_EVENT_ASYNC_BEGIN1("media", GetPrepareTraceString<StreamType>(), this,
+                           "timestamp_us",
+                           output->timestamp().InMicroseconds());
   preparing_output_ = true;
   prepare_cb_.Run(
-      unprepared_outputs_.front(),
-      base::BindOnce(&DecoderStream<StreamType>::OnPreparedOutputReady,
-                     prepare_weak_factory_.GetWeakPtr()));
+      output, base::BindOnce(&DecoderStream<StreamType>::OnPreparedOutputReady,
+                             prepare_weak_factory_.GetWeakPtr()));
 }
 
 template <DemuxerStream::Type StreamType>
diff --git a/media/filters/fuchsia/fuchsia_video_decoder.cc b/media/filters/fuchsia/fuchsia_video_decoder.cc
index 84c3f481..f749ce4 100644
--- a/media/filters/fuchsia/fuchsia_video_decoder.cc
+++ b/media/filters/fuchsia/fuchsia_video_decoder.cc
@@ -104,11 +104,19 @@
   CodecBuffer() = default;
 
   bool Initialize(const fuchsia::media::StreamBufferConstraints& constraints) {
-    size_ = constraints.per_packet_buffer_bytes_recommended;
+    if (!constraints.has_per_packet_buffer_bytes_recommended()) {
+      return false;
+    }
 
-    if (constraints.is_physically_contiguous_required) {
-      vmo_ =
-          CreateContiguousVmo(size_, constraints.very_temp_kludge_bti_handle);
+    size_ = *constraints.per_packet_buffer_bytes_recommended();
+
+    if (constraints.has_is_physically_contiguous_required() &&
+        *constraints.is_physically_contiguous_required()) {
+      if (!constraints.has_very_temp_kludge_bti_handle()) {
+        return false;
+      }
+      vmo_ = CreateContiguousVmo(size_,
+                                 *constraints.very_temp_kludge_bti_handle());
     } else {
       vmo_ = CreateVmo(size_);
     }
@@ -131,14 +139,14 @@
     }
 
     fuchsia::media::StreamBufferDataVmo buf_data;
-    buf_data.vmo_handle = std::move(vmo_dup);
+    buf_data.set_vmo_handle(std::move(vmo_dup));
 
-    buf_data.vmo_usable_start = 0;
-    buf_data.vmo_usable_size = size_;
+    buf_data.set_vmo_usable_start(0);
+    buf_data.set_vmo_usable_size(size_);
 
-    buffer->data.set_vmo(std::move(buf_data));
-    buffer->buffer_lifetime_ordinal = buffer_lifetime_ordinal;
-    buffer->buffer_index = buffer_index;
+    buffer->mutable_data()->set_vmo(std::move(buf_data));
+    buffer->set_buffer_lifetime_ordinal(buffer_lifetime_ordinal);
+    buffer->set_buffer_index(buffer_index);
 
     return true;
   }
@@ -374,23 +382,23 @@
   auto done_callback = BindToCurrentLoop(init_cb);
 
   fuchsia::mediacodec::CreateDecoder_Params codec_params;
-  codec_params.input_details.format_details_version_ordinal = 0;
+  codec_params.mutable_input_details()->set_format_details_version_ordinal(0);
 
   switch (config.codec()) {
     case kCodecH264:
-      codec_params.input_details.mime_type = "video/h264";
+      codec_params.mutable_input_details()->set_mime_type("video/h264");
       break;
     case kCodecVP8:
-      codec_params.input_details.mime_type = "video/vp8";
+      codec_params.mutable_input_details()->set_mime_type("video/vp8");
       break;
     case kCodecVP9:
-      codec_params.input_details.mime_type = "video/vp9";
+      codec_params.mutable_input_details()->set_mime_type("video/vp9");
       break;
     case kCodecHEVC:
-      codec_params.input_details.mime_type = "video/hevc";
+      codec_params.mutable_input_details()->set_mime_type("video/hevc");
       break;
     case kCodecAV1:
-      codec_params.input_details.mime_type = "video/av1";
+      codec_params.mutable_input_details()->set_mime_type("video/av1");
       break;
 
     default:
@@ -398,8 +406,8 @@
       return;
   }
 
-  codec_params.promise_separate_access_units_on_input = true;
-  codec_params.require_hw = !enable_sw_decoding_;
+  codec_params.set_promise_separate_access_units_on_input(true);
+  codec_params.set_require_hw(!enable_sw_decoding_);
 
   auto codec_factory =
       base::fuchsia::ServiceDirectoryClient::ForCurrentProcess()
@@ -503,23 +511,30 @@
 
 void FuchsiaVideoDecoder::OnFreeInputPacket(
     fuchsia::media::PacketHeader free_input_packet) {
-  if (free_input_packet.buffer_lifetime_ordinal !=
+  if (!free_input_packet.has_buffer_lifetime_ordinal() ||
+      !free_input_packet.has_packet_index()) {
+    DLOG(ERROR) << "Received OnFreeInputPacket() with missing required fields.";
+    OnError();
+    return;
+  }
+
+  if (*free_input_packet.buffer_lifetime_ordinal() !=
       input_buffer_lifetime_ordinal_) {
     return;
   }
 
-  if (free_input_packet.packet_index >= input_buffers_.size()) {
+  if (*free_input_packet.packet_index() >= input_buffers_.size()) {
     DLOG(ERROR) << "fuchsia.mediacodec sent OnFreeInputPacket() for an unknown "
                    "packet: buffer_lifetime_ordinal="
-                << free_input_packet.buffer_lifetime_ordinal
-                << " packet_index=" << free_input_packet.packet_index;
+                << *free_input_packet.buffer_lifetime_ordinal()
+                << " packet_index=" << *free_input_packet.packet_index();
     OnError();
     return;
   }
 
   DCHECK_GT(num_used_input_buffers_, 0);
   num_used_input_buffers_--;
-  input_buffers_[free_input_packet.packet_index].OnDoneDecoding(
+  input_buffers_[*free_input_packet.packet_index()].OnDoneDecoding(
       DecodeStatus::OK);
 
   // Try to pump input in case it was blocked.
@@ -528,32 +543,57 @@
 
 void FuchsiaVideoDecoder::OnOutputConfig(
     fuchsia::media::StreamOutputConfig output_config) {
-  if (output_config.stream_lifetime_ordinal != stream_lifetime_ordinal_)
+  if (!output_config.has_stream_lifetime_ordinal() ||
+      !output_config.has_format_details()) {
+    DLOG(ERROR) << "Received OnOutputConfig() with missing required fields.";
+    OnError();
     return;
+  }
 
-  auto& format = output_config.format_details;
+  if (*output_config.stream_lifetime_ordinal() != stream_lifetime_ordinal_) {
+    return;
+  }
 
-  if (!format.domain->is_video() || !format.domain->video().is_uncompressed()) {
+  auto& format = *output_config.mutable_format_details();
+
+  if (!format.has_domain() || !format.domain()->is_video() ||
+      !format.domain()->video().is_uncompressed()) {
     DLOG(ERROR) << "Received OnOutputConfig() with invalid format.";
     OnError();
     return;
   }
 
-  if (output_config.buffer_constraints_action_required) {
-    if (!InitializeOutputBuffers(std::move(output_config.buffer_constraints))) {
+  if (output_config.has_buffer_constraints_action_required() &&
+      *output_config.buffer_constraints_action_required()) {
+    if (!output_config.has_buffer_constraints()) {
+      DLOG(ERROR) << "Received OnOutputConfig() which requires buffer "
+                     "constraints action, but without buffer constraints.";
+      OnError();
+      return;
+    }
+    if (!InitializeOutputBuffers(
+            std::move(*output_config.mutable_buffer_constraints()))) {
       DLOG(ERROR) << "Failed to initialize output buffers.";
       OnError();
       return;
     }
   }
 
-  output_format_ = std::move(format.domain->video().uncompressed());
+  output_format_ = std::move(format.mutable_domain()->video().uncompressed());
 }
 
 void FuchsiaVideoDecoder::OnOutputPacket(fuchsia::media::Packet output_packet,
                                          bool error_detected_before,
                                          bool error_detected_during) {
-  if (output_packet.header.buffer_lifetime_ordinal !=
+  if (!output_packet.has_header() ||
+      !output_packet.header()->has_buffer_lifetime_ordinal() ||
+      !output_packet.header()->has_packet_index()) {
+    DLOG(ERROR) << "Received OnOutputPacket() with missing required fields.";
+    OnError();
+    return;
+  }
+
+  if (*output_packet.header()->buffer_lifetime_ordinal() !=
       output_buffer_lifetime_ordinal_) {
     return;
   }
@@ -598,15 +638,17 @@
   }
 
   if (!layout) {
-    codec_->RecycleOutputPacket(output_packet.header);
+    codec_->RecycleOutputPacket(fidl::Clone(*output_packet.header()));
     return;
   }
 
   base::TimeDelta timestamp;
-  if (output_packet.has_timestamp_ish)
-    timestamp = base::TimeDelta::FromNanoseconds(output_packet.timestamp_ish);
+  if (output_packet.has_timestamp_ish()) {
+    timestamp =
+        base::TimeDelta::FromNanoseconds(*output_packet.timestamp_ish());
+  }
 
-  auto packet_index = output_packet.header.packet_index;
+  auto packet_index = *output_packet.header()->packet_index();
   auto& buffer = output_buffers_[packet_index];
 
   DCHECK_LT(num_used_output_buffers_, static_cast<int>(output_buffers_.size()));
@@ -692,13 +734,22 @@
     fuchsia::media::StreamBufferConstraints constraints) {
   input_buffer_lifetime_ordinal_ += 2;
 
-  auto settings = constraints.default_settings;
-  settings.buffer_lifetime_ordinal = input_buffer_lifetime_ordinal_;
-  settings.packet_count_for_client = 0;
-  codec_->SetInputBufferSettings(settings);
+  if (!constraints.has_default_settings() ||
+      !constraints.default_settings()->has_packet_count_for_server() ||
+      !constraints.default_settings()->has_packet_count_for_client()) {
+    DLOG(ERROR)
+        << "Received InitializeInputBuffers() with missing required fields.";
+    OnError();
+    return false;
+  }
+
+  auto settings = fidl::Clone(*constraints.default_settings());
+  settings.set_buffer_lifetime_ordinal(input_buffer_lifetime_ordinal_);
+  settings.set_packet_count_for_client(0);
+  codec_->SetInputBufferSettings(fidl::Clone(settings));
 
   int total_buffers =
-      settings.packet_count_for_server + settings.packet_count_for_client;
+      *settings.packet_count_for_server() + *settings.packet_count_for_client();
   std::vector<InputBuffer> new_buffers(total_buffers);
 
   for (int i = 0; i < total_buffers; ++i) {
@@ -755,15 +806,16 @@
         input_buffer->FillFromDecodeBuffer(&pending_decodes_.front());
 
     fuchsia::media::Packet packet;
-    packet.header.buffer_lifetime_ordinal = input_buffer_lifetime_ordinal_;
-    packet.header.packet_index = input_buffer - input_buffers_.begin();
-    packet.buffer_index = packet.header.packet_index;
-    packet.has_timestamp_ish = true;
-    packet.timestamp_ish =
-        pending_decodes_.front().buffer().timestamp().InNanoseconds();
-    packet.stream_lifetime_ordinal = stream_lifetime_ordinal_;
-    packet.start_offset = 0;
-    packet.valid_length_bytes = bytes_filled;
+    packet.mutable_header()->set_buffer_lifetime_ordinal(
+        input_buffer_lifetime_ordinal_);
+    packet.mutable_header()->set_packet_index(input_buffer -
+                                              input_buffers_.begin());
+    packet.set_buffer_index(*packet.header()->packet_index());
+    packet.set_timestamp_ish(
+        pending_decodes_.front().buffer().timestamp().InNanoseconds());
+    packet.set_stream_lifetime_ordinal(stream_lifetime_ordinal_);
+    packet.set_start_offset(0);
+    packet.set_valid_length_bytes(bytes_filled);
 
     active_stream_ = true;
     codec_->QueueInputPacket(std::move(packet));
@@ -776,21 +828,31 @@
 
 bool FuchsiaVideoDecoder::InitializeOutputBuffers(
     fuchsia::media::StreamBufferConstraints constraints) {
+  if (!constraints.has_default_settings() ||
+      !constraints.has_packet_count_for_client_max() ||
+      !constraints.default_settings()->has_packet_count_for_server() ||
+      !constraints.default_settings()->has_packet_count_for_client()) {
+    DLOG(ERROR)
+        << "Received InitializeOutputBuffers() with missing required fields.";
+    OnError();
+    return false;
+  }
+
   // mediacodec API expects odd buffer lifetime ordinal, which is incremented by
   // 2 for each buffer generation.
   output_buffer_lifetime_ordinal_ += 2;
 
-  auto settings = constraints.default_settings;
-  settings.buffer_lifetime_ordinal = output_buffer_lifetime_ordinal_;
+  auto settings = fidl::Clone(*constraints.default_settings());
+  settings.set_buffer_lifetime_ordinal(output_buffer_lifetime_ordinal_);
 
-  max_used_output_buffers_ =
-      std::min(kMaxUsedOutputFrames, constraints.packet_count_for_client_max);
-  settings.packet_count_for_client = max_used_output_buffers_;
+  max_used_output_buffers_ = std::min(
+      kMaxUsedOutputFrames, *constraints.packet_count_for_client_max());
+  settings.set_packet_count_for_client(max_used_output_buffers_);
 
-  codec_->SetOutputBufferSettings(std::move(settings));
+  codec_->SetOutputBufferSettings(fidl::Clone(settings));
 
   int total_buffers =
-      settings.packet_count_for_server + settings.packet_count_for_client;
+      *settings.packet_count_for_server() + *settings.packet_count_for_client();
   std::vector<scoped_refptr<OutputBuffer>> new_buffers(total_buffers);
 
   for (int i = 0; i < total_buffers; ++i) {
@@ -821,8 +883,10 @@
   if (buffer_lifetime_ordinal == output_buffer_lifetime_ordinal_) {
     DCHECK_GT(num_used_output_buffers_, 0);
     num_used_output_buffers_--;
-    codec_->RecycleOutputPacket(
-        fuchsia::media::PacketHeader{buffer_lifetime_ordinal, packet_index});
+    fuchsia::media::PacketHeader header;
+    header.set_buffer_lifetime_ordinal(buffer_lifetime_ordinal);
+    header.set_packet_index(packet_index);
+    codec_->RecycleOutputPacket(std::move(header));
   }
 }
 
diff --git a/media/gpu/vaapi/BUILD.gn b/media/gpu/vaapi/BUILD.gn
index 0d0a626..1cf5c393d 100644
--- a/media/gpu/vaapi/BUILD.gn
+++ b/media/gpu/vaapi/BUILD.gn
@@ -143,8 +143,10 @@
     "//base",
     "//base/test:test_support",
     "//media:test_support",
+    "//skia",
     "//testing/gtest",
     "//third_party/libyuv:libyuv",
+    "//ui/gfx/codec",
     "//ui/gfx/geometry",
   ]
 }
diff --git a/media/gpu/vaapi/vaapi_jpeg_decoder.cc b/media/gpu/vaapi/vaapi_jpeg_decoder.cc
index ee16869..9f7da12 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decoder.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_decoder.cc
@@ -13,6 +13,7 @@
 #include <va/va.h>
 
 #include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/stl_util.h"
 #include "media/filters/jpeg_parser.h"
 #include "media/gpu/macros.h"
@@ -149,23 +150,39 @@
 // VAAPI only supports a subset of JPEG profiles. This function determines
 // whether a given parsed JPEG result is supported or not.
 static bool IsVaapiSupportedJpeg(const JpegParseResult& jpeg) {
-  if (jpeg.frame_header.visible_width < 1 ||
-      jpeg.frame_header.visible_height < 1) {
-    DLOG(ERROR) << "width(" << jpeg.frame_header.visible_width
-                << ") and height(" << jpeg.frame_header.visible_height
-                << ") should be at least 1";
+  // Validate the visible size.
+  if (jpeg.frame_header.visible_width == 0u) {
+    DLOG(ERROR) << "Visible width can't be zero";
+    return false;
+  }
+  if (jpeg.frame_header.visible_height == 0u) {
+    DLOG(ERROR) << "Visible height can't be zero";
     return false;
   }
 
-  // Size 64k*64k is the maximum in the JPEG standard. VAAPI doesn't support
-  // resolutions larger than 16k*16k.
-  constexpr int kMaxDimension = 16384;
-  if (jpeg.frame_header.coded_width > kMaxDimension ||
-      jpeg.frame_header.coded_height > kMaxDimension) {
-    DLOG(ERROR) << "VAAPI doesn't support size("
-                << jpeg.frame_header.coded_width << "*"
-                << jpeg.frame_header.coded_height << ") larger than "
-                << kMaxDimension << "*" << kMaxDimension;
+  // Validate the coded size.
+  gfx::Size min_jpeg_resolution;
+  if (!VaapiWrapper::GetJpegDecodeMinResolution(&min_jpeg_resolution)) {
+    DLOG(ERROR) << "Could not get the minimum resolution";
+    return false;
+  }
+  gfx::Size max_jpeg_resolution;
+  if (!VaapiWrapper::GetJpegDecodeMaxResolution(&max_jpeg_resolution)) {
+    DLOG(ERROR) << "Could not get the maximum resolution";
+    return false;
+  }
+  const int actual_jpeg_coded_width =
+      base::strict_cast<int>(jpeg.frame_header.coded_width);
+  const int actual_jpeg_coded_height =
+      base::strict_cast<int>(jpeg.frame_header.coded_height);
+  if (actual_jpeg_coded_width < min_jpeg_resolution.width() ||
+      actual_jpeg_coded_height < min_jpeg_resolution.height() ||
+      actual_jpeg_coded_width > max_jpeg_resolution.width() ||
+      actual_jpeg_coded_height > max_jpeg_resolution.height()) {
+    DLOG(ERROR) << "VAAPI doesn't support size " << actual_jpeg_coded_width
+                << "x" << actual_jpeg_coded_height << ": not in range "
+                << min_jpeg_resolution.ToString() << " - "
+                << max_jpeg_resolution.ToString();
     return false;
   }
 
diff --git a/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc b/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
index b5f1348..60a134d 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
@@ -32,6 +32,11 @@
 #include "media/gpu/vaapi/vaapi_utils.h"
 #include "media/gpu/vaapi/vaapi_wrapper.h"
 #include "third_party/libyuv/include/libyuv.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkPixmap.h"
+#include "third_party/skia/include/encode/SkJpegEncoder.h"
+#include "ui/gfx/codec/jpeg_codec.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace media {
@@ -62,6 +67,14 @@
     .bits_per_pixel = 12,
 };
 
+// The size of the minimum coded unit for a YUV 4:2:0 image (both the width and
+// the height of the MCU are the same for 4:2:0).
+constexpr int k420MCUSize = 16;
+
+// The largest maximum supported surface size we expect a driver to report for
+// JPEG decoding.
+constexpr gfx::Size kLargestSupportedSize(8192, 8192);
+
 // Compares the result of sw decoding |encoded_image| with |decoded_image| using
 // SSIM. Returns true if all conversions work and SSIM is above a given
 // threshold (kMinSsim), or false otherwise.
@@ -173,6 +186,113 @@
   return true;
 }
 
+// Generates a checkerboard pattern as a JPEG image of a specified |size| and
+// |subsampling| format. Returns an empty vector on failure.
+std::vector<unsigned char> GenerateJpegImage(
+    const gfx::Size& size,
+    SkJpegEncoder::Downsample subsampling = SkJpegEncoder::Downsample::k420) {
+  DCHECK(!size.IsEmpty());
+
+  // First build a raw RGBA image of the given size with a checkerboard pattern.
+  const SkImageInfo image_info = SkImageInfo::Make(
+      size.width(), size.height(), SkColorType::kRGBA_8888_SkColorType,
+      SkAlphaType::kOpaque_SkAlphaType);
+  const size_t byte_size = image_info.computeMinByteSize();
+  if (byte_size == SIZE_MAX)
+    return {};
+  const size_t stride = image_info.minRowBytes();
+  DCHECK_EQ(4, SkColorTypeBytesPerPixel(image_info.colorType()));
+  DCHECK_EQ(4 * size.width(), base::checked_cast<int>(stride));
+  constexpr gfx::Size kCheckerRectSize(3, 5);
+  std::vector<uint8_t> rgba_data(byte_size);
+  uint8_t* data = rgba_data.data();
+  for (int y = 0; y < size.height(); y++) {
+    const bool y_bit = (((y / kCheckerRectSize.height()) & 0x1) == 0);
+    for (int x = 0; x < base::checked_cast<int>(stride); x += 4) {
+      const bool x_bit = (((x / kCheckerRectSize.width()) & 0x1) == 0);
+      const SkColor color = (x_bit != y_bit) ? SK_ColorBLUE : SK_ColorMAGENTA;
+      data[x + 0] = SkColorGetR(color);
+      data[x + 1] = SkColorGetG(color);
+      data[x + 2] = SkColorGetB(color);
+      data[x + 3] = SkColorGetA(color);
+    }
+    data += stride;
+  }
+
+  // Now, encode it as a JPEG.
+  //
+  // TODO(andrescj): if this generates a large enough image (in terms of byte
+  // size), it will be decoded incorrectly in AMD Stoney Ridge (see
+  // b/127874877). When that's resolved, change the quality here to 100 so that
+  // the generated JPEG is large.
+  std::vector<unsigned char> jpeg_data;
+  if (gfx::JPEGCodec::Encode(
+          SkPixmap(image_info, rgba_data.data(), stride) /* input */,
+          95 /* quality */, subsampling /* downsample */,
+          &jpeg_data /* output */)) {
+    return jpeg_data;
+  }
+  return {};
+}
+
+// Rounds |n| to the greatest multiple of |m| that is less than or equal to |n|.
+int RoundDownToMultiple(int n, int m) {
+  DCHECK_GE(n, 0);
+  DCHECK_GT(m, 0);
+  return (n / m) * m;
+}
+
+// Rounds |n| to the smallest multiple of |m| that is greater than or equal to
+// |n|.
+int RoundUpToMultiple(int n, int m) {
+  DCHECK_GE(n, 0);
+  DCHECK_GT(m, 0);
+  if (n % m == 0)
+    return n;
+  base::CheckedNumeric<int> safe_n(n);
+  safe_n += m;
+  return RoundDownToMultiple(safe_n.ValueOrDie(), m);
+}
+
+// Given a minimum supported surface dimension (width or height) value
+// |min_surface_supported|, this function returns a non-zero coded dimension of
+// a 4:2:0 JPEG image that would not be supported because the dimension is right
+// below the supported value. For example, if |min_surface_supported| is 19,
+// this function should return 16 because for a 4:2:0 image, both coded
+// dimensions should be multiples of 16. If an unsupported dimension was found
+// (i.e., |min_surface_supported| > 16), this function returns true, false
+// otherwise.
+bool GetMinUnsupportedDimension(int min_surface_supported,
+                                int* min_unsupported) {
+  if (min_surface_supported <= k420MCUSize)
+    return false;
+  *min_unsupported =
+      RoundDownToMultiple(min_surface_supported - 1, k420MCUSize);
+  return true;
+}
+
+// Given a minimum supported surface dimension (width or height) value
+// |min_surface_supported|, this function returns a non-zero coded dimension of
+// a 4:2:0 JPEG image that would be supported because the dimension is at least
+// the minimum. For example, if |min_surface_supported| is 35, this function
+// should return 48 because for a 4:2:0 image, both coded dimensions should be
+// multiples of 16.
+int GetMinSupportedDimension(int min_surface_supported) {
+  if (min_surface_supported == 0)
+    return k420MCUSize;
+  return RoundUpToMultiple(min_surface_supported, k420MCUSize);
+}
+
+// Given a maximum supported surface dimension (width or height) value
+// |max_surface_supported|, this function returns the coded dimension of a 4:2:0
+// JPEG image that would be supported because the dimension is at most the
+// maximum. For example, if |max_surface_supported| is 65, this function
+// should return 64 because for a 4:2:0 image, both coded dimensions should be
+// multiples of 16.
+int GetMaxSupportedDimension(int max_surface_supported) {
+  return RoundDownToMultiple(max_surface_supported, k420MCUSize);
+}
+
 }  // namespace
 
 class VaapiJpegDecoderTest : public testing::TestWithParam<TestParam> {
@@ -191,7 +311,8 @@
   base::FilePath FindTestDataFilePath(const std::string& file_name);
 
   std::unique_ptr<ScopedVAImage> Decode(
-      base::span<const uint8_t> encoded_image);
+      base::span<const uint8_t> encoded_image,
+      VaapiJpegDecodeStatus* status = nullptr);
 
   base::Lock* GetVaapiWrapperLock() const
       LOCK_RETURNED(decoder_.vaapi_wrapper_->va_lock_) {
@@ -223,15 +344,18 @@
 }
 
 std::unique_ptr<ScopedVAImage> VaapiJpegDecoderTest::Decode(
-    base::span<const uint8_t> encoded_image) {
-  VaapiJpegDecodeStatus status;
+    base::span<const uint8_t> encoded_image,
+    VaapiJpegDecodeStatus* status) {
+  VaapiJpegDecodeStatus tmp_status;
   std::unique_ptr<ScopedVAImage> scoped_image =
-      decoder_.DoDecode(encoded_image, &status);
-  EXPECT_EQ(!!scoped_image, status == VaapiJpegDecodeStatus::kSuccess);
+      decoder_.DoDecode(encoded_image, &tmp_status);
+  EXPECT_EQ(!!scoped_image, tmp_status == VaapiJpegDecodeStatus::kSuccess);
+  if (status)
+    *status = tmp_status;
   return scoped_image;
 }
 
-TEST_P(VaapiJpegDecoderTest, DecodeSuccess) {
+TEST_P(VaapiJpegDecoderTest, DecodeSucceeds) {
   base::FilePath input_file = FindTestDataFilePath(GetParam().filename);
   std::string jpeg_data;
   ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data))
@@ -245,14 +369,151 @@
   ASSERT_TRUE(CompareImages(encoded_image, scoped_image.get()));
 }
 
-TEST_F(VaapiJpegDecoderTest, DecodeFail) {
+// Make sure that JPEGs whose size is in the supported size range are decoded
+// successfully.
+//
+// TODO(andrescj): for now, this assumes 4:2:0. Handle other formats.
+TEST_F(VaapiJpegDecoderTest, DecodeSucceedsForSupportedSizes) {
+  gfx::Size min_supported_size;
+  ASSERT_TRUE(VaapiWrapper::GetJpegDecodeMinResolution(&min_supported_size));
+  gfx::Size max_supported_size;
+  ASSERT_TRUE(VaapiWrapper::GetJpegDecodeMaxResolution(&max_supported_size));
+
+  // Ensure the maximum supported size is reasonable.
+  ASSERT_GE(max_supported_size.width(), min_supported_size.width());
+  ASSERT_GE(max_supported_size.height(), min_supported_size.height());
+  ASSERT_LE(max_supported_size.width(), kLargestSupportedSize.width());
+  ASSERT_LE(max_supported_size.height(), kLargestSupportedSize.height());
+
+  // The actual image min/max coded size depends on the subsampling format. For
+  // example, for 4:2:0, the coded dimensions must be multiples of 16. So, if
+  // the minimum surface size is, e.g., 18x18, the minimum image coded size is
+  // 32x32. Get those actual min/max coded sizes now.
+  const int min_width = GetMinSupportedDimension(min_supported_size.width());
+  const int min_height = GetMinSupportedDimension(min_supported_size.height());
+  const int max_width = GetMaxSupportedDimension(max_supported_size.width());
+  const int max_height = GetMaxSupportedDimension(max_supported_size.height());
+  const std::vector<gfx::Size> test_sizes = {{min_width, min_height},
+                                             {min_width, max_height},
+                                             {max_width, min_height},
+                                             {max_width, max_height}};
+  for (const auto& test_size : test_sizes) {
+    const std::vector<unsigned char> jpeg_data =
+        GenerateJpegImage(gfx::Size(test_size.width(), test_size.height()));
+    auto jpeg_data_span =
+        base::make_span<const uint8_t>(jpeg_data.data(), jpeg_data.size());
+    ASSERT_FALSE(jpeg_data.empty());
+    std::unique_ptr<ScopedVAImage> scoped_image = Decode(jpeg_data_span);
+    ASSERT_TRUE(scoped_image)
+        << "Decode unexpectedly failed for size = " << test_size.ToString();
+    EXPECT_TRUE(CompareImages(jpeg_data_span, scoped_image.get()))
+        << "The SSIM check unexpectedly failed for size = "
+        << test_size.ToString();
+  }
+}
+
+// Make sure that JPEGs whose size is below the supported size range are
+// rejected.
+//
+// TODO(andrescj): for now, this assumes 4:2:0. Handle other formats.
+TEST_F(VaapiJpegDecoderTest, DecodeFailsForBelowMinSize) {
+  gfx::Size min_supported_size;
+  ASSERT_TRUE(VaapiWrapper::GetJpegDecodeMinResolution(&min_supported_size));
+  gfx::Size max_supported_size;
+  ASSERT_TRUE(VaapiWrapper::GetJpegDecodeMaxResolution(&max_supported_size));
+
+  // Get good (supported) minimum dimensions.
+  const int good_width = GetMinSupportedDimension(min_supported_size.width());
+  ASSERT_LE(good_width, max_supported_size.width());
+  const int good_height = GetMinSupportedDimension(min_supported_size.height());
+  ASSERT_LE(good_height, max_supported_size.height());
+
+  // Get bad (unsupported) dimensions.
+  int bad_width;
+  const bool got_bad_width =
+      GetMinUnsupportedDimension(min_supported_size.width(), &bad_width);
+  int bad_height;
+  const bool got_bad_height =
+      GetMinUnsupportedDimension(min_supported_size.height(), &bad_height);
+
+  // Now build and test the good/bad combinations that we expect will fail.
+  std::vector<gfx::Size> test_sizes;
+  if (got_bad_width)
+    test_sizes.push_back({bad_width, good_height});
+  if (got_bad_height)
+    test_sizes.push_back({good_width, bad_height});
+  if (got_bad_width && got_bad_height)
+    test_sizes.push_back({bad_width, bad_height});
+  for (const auto& test_size : test_sizes) {
+    const std::vector<unsigned char> jpeg_data =
+        GenerateJpegImage(gfx::Size(test_size.width(), test_size.height()));
+    ASSERT_FALSE(jpeg_data.empty());
+    VaapiJpegDecodeStatus status = VaapiJpegDecodeStatus::kSuccess;
+    ASSERT_FALSE(Decode(
+        base::make_span<const uint8_t>(jpeg_data.data(), jpeg_data.size()),
+        &status))
+        << "Decode unexpectedly succeeded for size = " << test_size.ToString();
+    EXPECT_EQ(VaapiJpegDecodeStatus::kUnsupportedJpeg, status);
+  }
+}
+
+// Make sure that JPEGs whose size is above the supported size range are
+// rejected.
+//
+// TODO(andrescj): for now, this assumes 4:2:0. Handle other formats.
+TEST_F(VaapiJpegDecoderTest, DecodeFailsForAboveMaxSize) {
+  gfx::Size min_supported_size;
+  ASSERT_TRUE(VaapiWrapper::GetJpegDecodeMinResolution(&min_supported_size));
+  gfx::Size max_supported_size;
+  ASSERT_TRUE(VaapiWrapper::GetJpegDecodeMaxResolution(&max_supported_size));
+
+  // Ensure the maximum supported size is reasonable.
+  ASSERT_GE(max_supported_size.width(), min_supported_size.width());
+  ASSERT_GE(max_supported_size.height(), min_supported_size.height());
+  ASSERT_LE(max_supported_size.width(), kLargestSupportedSize.width());
+  ASSERT_LE(max_supported_size.height(), kLargestSupportedSize.height());
+
+  // Get good (supported) maximum dimensions.
+  const int good_width = GetMaxSupportedDimension(max_supported_size.width());
+  ASSERT_GE(good_width, min_supported_size.width());
+  const int good_height = GetMaxSupportedDimension(max_supported_size.height());
+  ASSERT_GE(good_height, min_supported_size.height());
+
+  // Get bad (unsupported) dimensions.
+  int bad_width =
+      RoundUpToMultiple(max_supported_size.width() + 1, k420MCUSize);
+  int bad_height =
+      RoundUpToMultiple(max_supported_size.height() + 1, k420MCUSize);
+
+  // Now build and test the good/bad combinations that we expect will fail.
+  const std::vector<gfx::Size> test_sizes = {{bad_width, good_height},
+                                             {good_width, bad_height},
+                                             {bad_width, bad_height}};
+  for (const auto& test_size : test_sizes) {
+    const std::vector<unsigned char> jpeg_data =
+        GenerateJpegImage(gfx::Size(test_size.width(), test_size.height()));
+    ASSERT_FALSE(jpeg_data.empty());
+    VaapiJpegDecodeStatus status = VaapiJpegDecodeStatus::kSuccess;
+    ASSERT_FALSE(Decode(
+        base::make_span<const uint8_t>(jpeg_data.data(), jpeg_data.size()),
+        &status))
+        << "Decode unexpectedly succeeded for size = " << test_size.ToString();
+    EXPECT_EQ(VaapiJpegDecodeStatus::kUnsupportedJpeg, status);
+  }
+}
+
+TEST_F(VaapiJpegDecoderTest, DecodeFails) {
   // Not supported by VAAPI.
   base::FilePath input_file = FindTestDataFilePath(kUnsupportedFilename);
   std::string jpeg_data;
   ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data))
       << "failed to read input data from " << input_file.value();
-  EXPECT_FALSE(Decode(base::make_span<const uint8_t>(
-      reinterpret_cast<const uint8_t*>(jpeg_data.data()), jpeg_data.size())));
+  VaapiJpegDecodeStatus status = VaapiJpegDecodeStatus::kSuccess;
+  ASSERT_FALSE(Decode(
+      base::make_span<const uint8_t>(
+          reinterpret_cast<const uint8_t*>(jpeg_data.data()), jpeg_data.size()),
+      &status));
+  EXPECT_EQ(VaapiJpegDecodeStatus::kUnsupportedJpeg, status);
 }
 
 // This test exercises the usual ScopedVAImage lifetime.
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index e178115..a634f4a1 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -4,6 +4,7 @@
 
 #include "media/gpu/vaapi/vaapi_wrapper.h"
 
+#include <algorithm>
 #include <type_traits>
 
 #include <dlfcn.h>
@@ -436,6 +437,7 @@
  public:
   struct ProfileInfo {
     VAProfile va_profile;
+    gfx::Size min_resolution;
     gfx::Size max_resolution;
   };
   static const VASupportedProfiles& Get();
@@ -443,8 +445,12 @@
   const std::vector<ProfileInfo>& GetSupportedProfileInfosForCodecMode(
       VaapiWrapper::CodecMode mode) const;
 
+  // Determines if the |va_profile| is supported for |mode|. If so (and
+  // |profile_info| is not nullptr), *|profile_info| is filled with the profile
+  // information.
   bool IsProfileSupported(VaapiWrapper::CodecMode mode,
-                          VAProfile va_profile) const;
+                          VAProfile va_profile,
+                          ProfileInfo* profile_info = nullptr) const;
 
  private:
   friend class base::NoDestructor<VASupportedProfiles>;
@@ -462,6 +468,7 @@
   bool IsEntrypointSupported_Locked(VAProfile va_profile,
                                     VAEntrypoint entrypoint) const
       EXCLUSIVE_LOCKS_REQUIRED(va_lock_);
+
   // Returns true if |va_profile| for |entrypoint| with |required_attribs| is
   // supported.
   bool AreAttribsSupported_Locked(
@@ -469,14 +476,17 @@
       VAEntrypoint entrypoint,
       const std::vector<VAConfigAttrib>& required_attribs) const
       EXCLUSIVE_LOCKS_REQUIRED(va_lock_);
-  // Gets maximum resolution for |va_profile| and |entrypoint| with
-  // |required_attribs|. If return value is true, |resolution| is the maximum
-  // resolution.
-  bool GetMaxResolution_Locked(VAProfile va_profile,
-                               VAEntrypoint entrypoint,
-                               std::vector<VAConfigAttrib>& required_attribs,
-                               gfx::Size* resolution) const
+
+  // Fills |profile_info| for |va_profile| and |entrypoint| with
+  // |required_attribs|. If the return value is true, the operation was
+  // successful. Otherwise, the information in *|profile_info| shouldn't be
+  // relied upon.
+  bool FillProfileInfo_Locked(VAProfile va_profile,
+                              VAEntrypoint entrypoint,
+                              std::vector<VAConfigAttrib>& required_attribs,
+                              ProfileInfo* profile_info) const
       EXCLUSIVE_LOCKS_REQUIRED(va_lock_);
+
   std::vector<ProfileInfo> supported_profiles_[VaapiWrapper::kCodecModeMax];
 
   // Pointer to VADisplayState's members |va_lock_| and its |va_display_|.
@@ -501,12 +511,16 @@
 }
 
 bool VASupportedProfiles::IsProfileSupported(VaapiWrapper::CodecMode mode,
-                                             VAProfile va_profile) const {
-  for (const auto& profile : supported_profiles_[mode]) {
-    if (profile.va_profile == va_profile)
-      return true;
-  }
-  return false;
+                                             VAProfile va_profile,
+                                             ProfileInfo* profile_info) const {
+  auto iter = std::find_if(supported_profiles_[mode].begin(),
+                           supported_profiles_[mode].end(),
+                           [va_profile](const ProfileInfo& profile) {
+                             return profile.va_profile == va_profile;
+                           });
+  if (profile_info && iter != supported_profiles_[mode].end())
+    *profile_info = *iter;
+  return iter != supported_profiles_[mode].end();
 }
 
 VASupportedProfiles::VASupportedProfiles()
@@ -569,13 +583,12 @@
       continue;
 
     ProfileInfo profile_info;
-    if (!GetMaxResolution_Locked(va_profile, entrypoint, required_attribs,
-                                 &profile_info.max_resolution)) {
-      LOG(ERROR) << "GetMaxResolution failed for va_profile " << va_profile
-                 << " and entrypoint " << entrypoint;
+    if (!FillProfileInfo_Locked(va_profile, entrypoint, required_attribs,
+                                &profile_info)) {
+      LOG(ERROR) << "FillProfileInfo_Locked failed for va_profile "
+                 << va_profile << " and entrypoint " << entrypoint;
       continue;
     }
-    profile_info.va_profile = va_profile;
     supported_profile_infos.push_back(profile_info);
   }
   return supported_profile_infos;
@@ -653,11 +666,11 @@
   return true;
 }
 
-bool VASupportedProfiles::GetMaxResolution_Locked(
+bool VASupportedProfiles::FillProfileInfo_Locked(
     VAProfile va_profile,
     VAEntrypoint entrypoint,
     std::vector<VAConfigAttrib>& required_attribs,
-    gfx::Size* resolution) const {
+    ProfileInfo* profile_info) const {
   va_lock_->AssertAcquired();
   VAConfigID va_config_id;
   VAStatus va_res =
@@ -692,15 +705,26 @@
                                     &num_attribs);
   VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false);
 
-  resolution->SetSize(0, 0);
+  profile_info->va_profile = va_profile;
+  profile_info->min_resolution = gfx::Size();
+  profile_info->max_resolution = gfx::Size();
   for (const auto& attrib : attrib_list) {
-    if (attrib.type == VASurfaceAttribMaxWidth)
-      resolution->set_width(attrib.value.value.i);
-    else if (attrib.type == VASurfaceAttribMaxHeight)
-      resolution->set_height(attrib.value.value.i);
+    if (attrib.type == VASurfaceAttribMaxWidth) {
+      profile_info->max_resolution.set_width(
+          base::strict_cast<int>(attrib.value.value.i));
+    } else if (attrib.type == VASurfaceAttribMaxHeight) {
+      profile_info->max_resolution.set_height(
+          base::strict_cast<int>(attrib.value.value.i));
+    } else if (attrib.type == VASurfaceAttribMinWidth) {
+      profile_info->min_resolution.set_width(
+          base::strict_cast<int>(attrib.value.value.i));
+    } else if (attrib.type == VASurfaceAttribMinHeight) {
+      profile_info->min_resolution.set_height(
+          base::strict_cast<int>(attrib.value.value.i));
+    }
   }
-  if (resolution->IsEmpty()) {
-    LOG(ERROR) << "Wrong codec resolution: " << resolution->ToString();
+  if (profile_info->max_resolution.IsEmpty()) {
+    LOG(ERROR) << "Empty codec maximum resolution";
     return false;
   }
   return true;
@@ -918,6 +942,28 @@
 }
 
 // static
+bool VaapiWrapper::GetJpegDecodeMinResolution(gfx::Size* min_size) {
+  VASupportedProfiles::ProfileInfo profile_info;
+  if (!VASupportedProfiles::Get().IsProfileSupported(
+          kDecode, VAProfileJPEGBaseline, &profile_info)) {
+    return false;
+  }
+  *min_size = profile_info.min_resolution;
+  return true;
+}
+
+// static
+bool VaapiWrapper::GetJpegDecodeMaxResolution(gfx::Size* max_size) {
+  VASupportedProfiles::ProfileInfo profile_info;
+  if (!VASupportedProfiles::Get().IsProfileSupported(
+          kDecode, VAProfileJPEGBaseline, &profile_info)) {
+    return false;
+  }
+  *max_size = profile_info.max_resolution;
+  return true;
+}
+
+// static
 bool VaapiWrapper::IsJpegEncodeSupported() {
   return VASupportedProfiles::Get().IsProfileSupported(kEncode,
                                                        VAProfileJPEGBaseline);
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h
index 0b7751ba..07c6f4d8 100644
--- a/media/gpu/vaapi/vaapi_wrapper.h
+++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -90,6 +90,17 @@
   // Return true when JPEG decode is supported.
   static bool IsJpegDecodeSupported();
 
+  // Gets the minimum surface size allowed for JPEG decoding. Returns true if
+  // the size can be obtained, false otherwise. If a dimension is not reported
+  // by the driver, the dimension is returned as 0.
+  static bool GetJpegDecodeMinResolution(gfx::Size* min_size);
+
+  // Gets the maximum surface size allowed for JPEG decoding. Returns true if
+  // the size can be obtained, false otherwise. Because of the initialization in
+  // VASupportedProfiles::FillProfileInfo_Locked(), the size is guaranteed to
+  // not be empty (as long as this method returns true).
+  static bool GetJpegDecodeMaxResolution(gfx::Size* max_size);
+
   // Return true when JPEG encode is supported.
   static bool IsJpegEncodeSupported();
 
diff --git a/media/media_options.gni b/media/media_options.gni
index 179bd32..d5902db 100644
--- a/media/media_options.gni
+++ b/media/media_options.gni
@@ -84,10 +84,6 @@
   # are combined and we could override more logging than expected.
   enable_logging_override = !use_jumbo_build && is_chromecast
 
-  # If true, use cast CMA backend instead of default chromium media pipeline.
-  # TODO(sanfin): Remove this flag when all builds enable CMA.
-  is_cast_using_cma_backend = true
-
   # Dav1d is only enabled when av1 decoding is enabled.
   if (is_win && target_cpu == "arm64") {
     # TODO: Enable dav1d for Windows ARM64. https://crbug.com/941022
@@ -198,28 +194,16 @@
 # Can be overridden by gn build arguments from the --args command line flag
 # for local testing.
 if (enable_mojo_media) {
-  if (is_chromecast && is_cast_using_cma_backend && !is_android) {
+  if (is_chromecast) {
+    _default_mojo_media_services = cast_mojo_media_services
+    _default_mojo_media_host = cast_mojo_media_host
+  } else if (is_android) {
     _default_mojo_media_services = [
       "cdm",
-      "renderer",
+      "audio_decoder",
+      "video_decoder",
     ]
-    _default_mojo_media_host = "browser"
-  } else if (is_android) {
-    # Both chrome for Android and cast for Android belongs to this case
-    if (is_cast_audio_only) {
-      _default_mojo_media_services = [
-        "cdm",
-        "audio_decoder",
-      ]
-      _default_mojo_media_host = "browser"
-    } else {
-      _default_mojo_media_services = [
-        "cdm",
-        "audio_decoder",
-        "video_decoder",
-      ]
-      _default_mojo_media_host = "gpu"
-    }
+    _default_mojo_media_host = "gpu"
   } else if (is_chromeos || is_mac || is_win ||
              (is_desktop_linux && use_vaapi)) {
     _default_mojo_media_services = [ "video_decoder" ]
diff --git a/media/renderers/default_decoder_factory.cc b/media/renderers/default_decoder_factory.cc
index 30aee795d..55a9b09 100644
--- a/media/renderers/default_decoder_factory.cc
+++ b/media/renderers/default_decoder_factory.cc
@@ -23,6 +23,10 @@
 #include "media/filters/decrypting_video_decoder.h"
 #endif
 
+#if defined(OS_FUCHSIA)
+#include "media/filters/fuchsia/fuchsia_video_decoder.h"
+#endif
+
 #if BUILDFLAG(ENABLE_AV1_DECODER)
 #include "media/filters/aom_video_decoder.h"
 #endif
@@ -110,6 +114,10 @@
     }
   }
 
+#if defined(OS_FUCHSIA)
+  video_decoders->push_back(CreateFuchsiaVideoDecoder());
+#endif
+
 #if BUILDFLAG(ENABLE_LIBVPX)
   video_decoders->push_back(std::make_unique<OffloadingVpxVideoDecoder>());
 #endif
diff --git a/mojo/public/cpp/base/values_mojom_traits.h b/mojo/public/cpp/base/values_mojom_traits.h
index cdb9bbb..66752b7 100644
--- a/mojo/public/cpp/base/values_mojom_traits.h
+++ b/mojo/public/cpp/base/values_mojom_traits.h
@@ -86,8 +86,13 @@
         return mojo_base::mojom::ValueDataView::Tag::DICTIONARY_VALUE;
       case base::Value::Type::LIST:
         return mojo_base::mojom::ValueDataView::Tag::LIST_VALUE;
+      // TODO(crbug.com/859477): Remove after root cause is found.
+      case base::Value::Type::DEAD:
+        CHECK(false);
+        return mojo_base::mojom::ValueDataView::Tag::NULL_VALUE;
     }
-    NOTREACHED();
+    // TODO(crbug.com/859477): Revert to NOTREACHED() after root cause is found.
+    CHECK(false);
     return mojo_base::mojom::ValueDataView::Tag::NULL_VALUE;
   }
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 23e524c..0c16cf8e 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1018,7 +1018,6 @@
       "quic/platform/impl/quic_socket_address_impl.h",
       "quic/platform/impl/quic_stack_trace_impl.h",
       "quic/platform/impl/quic_str_cat_impl.h",
-      "quic/platform/impl/quic_string_impl.h",
       "quic/platform/impl/quic_string_piece_impl.h",
       "quic/platform/impl/quic_text_utils_impl.h",
       "quic/properties_based_quic_server_info.cc",
@@ -1659,7 +1658,6 @@
       "third_party/quiche/src/quic/platform/api/quic_socket_address.h",
       "third_party/quiche/src/quic/platform/api/quic_stack_trace.h",
       "third_party/quiche/src/quic/platform/api/quic_str_cat.h",
-      "third_party/quiche/src/quic/platform/api/quic_string.h",
       "third_party/quiche/src/quic/platform/api/quic_string_piece.h",
       "third_party/quiche/src/quic/platform/api/quic_text_utils.h",
       "third_party/quiche/src/spdy/core/hpack/hpack_constants.cc",
diff --git a/net/base/mime_sniffer.cc b/net/base/mime_sniffer.cc
index 6741cc8..8e361aff 100644
--- a/net/base/mime_sniffer.cc
+++ b/net/base/mime_sniffer.cc
@@ -215,7 +215,6 @@
 static const MagicNumber kExtraMagicNumbers[] = {
   MAGIC_NUMBER("image/x-xbitmap", "#define"),
   MAGIC_NUMBER("image/x-icon", "\x00\x00\x01\x00"),
-  MAGIC_NUMBER("image/svg+xml", "<?xml_version="),
   MAGIC_NUMBER("audio/wav", "RIFF....WAVEfmt "),
   MAGIC_NUMBER("video/avi", "RIFF....AVI LIST"),
   MAGIC_NUMBER("audio/ogg", "OggS\0"),
diff --git a/net/quic/platform/impl/quic_file_utils_impl.h b/net/quic/platform/impl/quic_file_utils_impl.h
index 41e9dd9..8fa98fd 100644
--- a/net/quic/platform/impl/quic_file_utils_impl.h
+++ b/net/quic/platform/impl/quic_file_utils_impl.h
@@ -9,7 +9,6 @@
 
 #include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 
 using base::FilePath;
diff --git a/net/quic/platform/impl/quic_flags_impl.h b/net/quic/platform/impl/quic_flags_impl.h
index 184c18a..6ec16b7b 100644
--- a/net/quic/platform/impl/quic_flags_impl.h
+++ b/net/quic/platform/impl/quic_flags_impl.h
@@ -16,7 +16,6 @@
 #include "base/no_destructor.h"
 #include "base/optional.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 
 #define QUIC_FLAG(type, flag, value) QUIC_EXPORT_PRIVATE extern type flag;
diff --git a/net/quic/platform/impl/quic_flags_test.cc b/net/quic/platform/impl/quic_flags_test.cc
index d5f3d7c6..aeb1599a 100644
--- a/net/quic/platform/impl/quic_flags_test.cc
+++ b/net/quic/platform/impl/quic_flags_test.cc
@@ -12,7 +12,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "net/quic/platform/impl/quic_flags_impl.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 
 DEFINE_QUIC_COMMAND_LINE_FLAG(bool, foo, false, "An old silent pond...");
diff --git a/net/quic/platform/impl/quic_hostname_utils_impl.h b/net/quic/platform/impl/quic_hostname_utils_impl.h
index a168a58..c7feb8e 100644
--- a/net/quic/platform/impl/quic_hostname_utils_impl.h
+++ b/net/quic/platform/impl/quic_hostname_utils_impl.h
@@ -7,7 +7,6 @@
 
 #include "base/macros.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 
 namespace quic {
diff --git a/net/quic/platform/impl/quic_linux_socket_utils_test.cc b/net/quic/platform/impl/quic_linux_socket_utils_test.cc
index 6be5762..7ce9c83 100644
--- a/net/quic/platform/impl/quic_linux_socket_utils_test.cc
+++ b/net/quic/platform/impl/quic_linux_socket_utils_test.cc
@@ -9,7 +9,6 @@
 #include <sstream>
 #include <vector>
 
-#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 
 using testing::_;
diff --git a/net/quic/platform/impl/quic_string_impl.h b/net/quic/platform/impl/quic_string_impl.h
deleted file mode 100644
index 5d81962d..0000000
--- a/net/quic/platform/impl/quic_string_impl.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_PLATFORM_IMPL_QUIC_STRING_IMPL_H_
-#define NET_QUIC_PLATFORM_IMPL_QUIC_STRING_IMPL_H_
-
-#include <string>
-
-namespace quic {
-
-using QuicStringImpl = std::string;
-
-}  // namespace quic
-
-#endif  // NET_QUIC_PLATFORM_IMPL_QUIC_STRING_IMPL_H_
diff --git a/net/quic/platform/impl/quic_string_utils_impl.h b/net/quic/platform/impl/quic_string_utils_impl.h
index b737f752..bb0d2e2c 100644
--- a/net/quic/platform/impl/quic_string_utils_impl.h
+++ b/net/quic/platform/impl/quic_string_utils_impl.h
@@ -12,7 +12,6 @@
 #include "base/strings/stringprintf.h"
 #include "net/base/hex_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 
 namespace quic {
diff --git a/net/quic/platform/impl/quic_thread_impl.h b/net/quic/platform/impl/quic_thread_impl.h
index ecb614d7..e4a788ad 100644
--- a/net/quic/platform/impl/quic_thread_impl.h
+++ b/net/quic/platform/impl/quic_thread_impl.h
@@ -6,7 +6,6 @@
 #define NET_QUIC_PLATFORM_IMPL_QUIC_THREAD_IMPL_H_
 
 #include "base/threading/simple_thread.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 
 namespace quic {
 
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index ac1a62a..07f36a3 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -300,3 +300,7 @@
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_always_reset_short_header_packets,
           false)
+
+// In QUIC, do not close connection if received an in-order ACK with decreased
+// largest_acked.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_tolerate_reneging, false)
diff --git a/net/server/http_server.cc b/net/server/http_server.cc
index 0b77834..9b9116d 100644
--- a/net/server/http_server.cc
+++ b/net/server/http_server.cc
@@ -257,7 +257,7 @@
         Close(connection->id());
         return ERR_CONNECTION_CLOSED;
       }
-      delegate_->OnWebSocketMessage(connection->id(), message);
+      delegate_->OnWebSocketMessage(connection->id(), std::move(message));
       if (HasClosedConnection(connection))
         return ERR_CONNECTION_CLOSED;
       continue;
diff --git a/net/server/http_server.h b/net/server/http_server.h
index edf90e2e..c6e2554c 100644
--- a/net/server/http_server.h
+++ b/net/server/http_server.h
@@ -40,8 +40,7 @@
                                const HttpServerRequestInfo& info) = 0;
     virtual void OnWebSocketRequest(int connection_id,
                                     const HttpServerRequestInfo& info) = 0;
-    virtual void OnWebSocketMessage(int connection_id,
-                                    const std::string& data) = 0;
+    virtual void OnWebSocketMessage(int connection_id, std::string data) = 0;
     virtual void OnClose(int connection_id) = 0;
   };
 
diff --git a/net/server/http_server_fuzzer.cc b/net/server/http_server_fuzzer.cc
index 7ccfbc23..53e0899 100644
--- a/net/server/http_server_fuzzer.cc
+++ b/net/server/http_server_fuzzer.cc
@@ -56,7 +56,7 @@
                                TRAFFIC_ANNOTATION_FOR_TESTS);
   }
 
-  void OnWebSocketMessage(int connection_id, const std::string& data) override {
+  void OnWebSocketMessage(int connection_id, std::string data) override {
     if (!(action_flags_ & ACCEPT_MESSAGE)) {
       server_->Close(connection_id);
       return;
diff --git a/net/server/http_server_unittest.cc b/net/server/http_server_unittest.cc
index 06de102f..cc7d954c 100644
--- a/net/server/http_server_unittest.cc
+++ b/net/server/http_server_unittest.cc
@@ -206,7 +206,7 @@
     NOTREACHED();
   }
 
-  void OnWebSocketMessage(int connection_id, const std::string& data) override {
+  void OnWebSocketMessage(int connection_id, std::string data) override {
     NOTREACHED();
   }
 
@@ -285,8 +285,7 @@
     HttpServerTest::OnHttpRequest(connection_id, info);
   }
 
-  void OnWebSocketMessage(int connection_id, const std::string& data) override {
-  }
+  void OnWebSocketMessage(int connection_id, std::string data) override {}
 };
 
 TEST_F(HttpServerTest, Request) {
diff --git a/ppapi/shared_impl/private/ppb_x509_certificate_private_shared.cc b/ppapi/shared_impl/private/ppb_x509_certificate_private_shared.cc
index 6ffff36..7f392d50 100644
--- a/ppapi/shared_impl/private/ppb_x509_certificate_private_shared.cc
+++ b/ppapi/shared_impl/private/ppb_x509_certificate_private_shared.cc
@@ -73,6 +73,8 @@
     }
     case base::Value::Type::DICTIONARY:
     case base::Value::Type::LIST:
+    // TODO(crbug.com/859477): Remove after root cause is found.
+    case base::Value::Type::DEAD:
       // Not handled.
       break;
   }
diff --git a/services/device/generic_sensor/linux/sensor_data_linux.cc b/services/device/generic_sensor/linux/sensor_data_linux.cc
index 0a69f39..e68af0db 100644
--- a/services/device/generic_sensor/linux/sensor_data_linux.cc
+++ b/services/device/generic_sensor/linux/sensor_data_linux.cc
@@ -213,4 +213,6 @@
 
 SensorInfoLinux::~SensorInfoLinux() = default;
 
+SensorInfoLinux::SensorInfoLinux(const SensorInfoLinux&) = default;
+
 }  // namespace device
diff --git a/services/device/generic_sensor/linux/sensor_data_linux.h b/services/device/generic_sensor/linux/sensor_data_linux.h
index 2be71af..9577dbb 100644
--- a/services/device/generic_sensor/linux/sensor_data_linux.h
+++ b/services/device/generic_sensor/linux/sensor_data_linux.h
@@ -73,6 +73,8 @@
                   SensorPathsLinux::ReaderFunctor scaling_func,
                   std::vector<base::FilePath> iio_device_reading_files);
   ~SensorInfoLinux();
+
+  SensorInfoLinux(const SensorInfoLinux&);
 };
 
 }  // namespace device
diff --git a/services/device/generic_sensor/platform_sensor_linux.cc b/services/device/generic_sensor/platform_sensor_linux.cc
index 77bbda1d..9b0fc5d 100644
--- a/services/device/generic_sensor/platform_sensor_linux.cc
+++ b/services/device/generic_sensor/platform_sensor_linux.cc
@@ -5,7 +5,6 @@
 #include "services/device/generic_sensor/platform_sensor_linux.h"
 
 #include "base/bind.h"
-#include "base/single_thread_task_runner.h"
 #include "services/device/generic_sensor/linux/sensor_data_linux.h"
 #include "services/device/generic_sensor/platform_sensor_reader_linux.h"
 
@@ -28,21 +27,18 @@
     mojom::SensorType type,
     SensorReadingSharedBuffer* reading_buffer,
     PlatformSensorProvider* provider,
-    const SensorInfoLinux* sensor_device,
-    scoped_refptr<base::SingleThreadTaskRunner> polling_thread_task_runner)
+    const SensorInfoLinux* sensor_device)
     : PlatformSensor(type, reading_buffer, provider),
       default_configuration_(
           PlatformSensorConfiguration(sensor_device->device_frequency)),
       reporting_mode_(sensor_device->reporting_mode),
-      polling_thread_task_runner_(std::move(polling_thread_task_runner)),
       weak_factory_(this) {
   sensor_reader_ = SensorReader::Create(
-      sensor_device, weak_factory_.GetWeakPtr(), task_runner_);
+      *sensor_device, weak_factory_.GetWeakPtr(), task_runner_);
 }
 
 PlatformSensorLinux::~PlatformSensorLinux() {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  polling_thread_task_runner_->DeleteSoon(FROM_HERE, sensor_reader_.release());
 }
 
 mojom::ReportingMode PlatformSensorLinux::GetReportingMode() {
@@ -70,18 +66,13 @@
 bool PlatformSensorLinux::StartSensor(
     const PlatformSensorConfiguration& configuration) {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  polling_thread_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&SensorReader::StartFetchingData,
-                     base::Unretained(sensor_reader_.get()), configuration));
+  sensor_reader_->StartFetchingData(configuration);
   return true;
 }
 
 void PlatformSensorLinux::StopSensor() {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  polling_thread_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&SensorReader::StopFetchingData,
-                                base::Unretained(sensor_reader_.get())));
+  sensor_reader_->StopFetchingData();
 }
 
 bool PlatformSensorLinux::CheckSensorConfiguration(
diff --git a/services/device/generic_sensor/platform_sensor_linux.h b/services/device/generic_sensor/platform_sensor_linux.h
index 7d2337e..c50345a 100644
--- a/services/device/generic_sensor/platform_sensor_linux.h
+++ b/services/device/generic_sensor/platform_sensor_linux.h
@@ -7,10 +7,6 @@
 
 #include "services/device/generic_sensor/platform_sensor.h"
 
-namespace base {
-class SingleThreadTaskRunner;
-}
-
 namespace device {
 
 class SensorReader;
@@ -18,12 +14,10 @@
 
 class PlatformSensorLinux : public PlatformSensor {
  public:
-  PlatformSensorLinux(
-      mojom::SensorType type,
-      SensorReadingSharedBuffer* reading_buffer,
-      PlatformSensorProvider* provider,
-      const SensorInfoLinux* sensor_device,
-      scoped_refptr<base::SingleThreadTaskRunner> polling_thread_task_runner);
+  PlatformSensorLinux(mojom::SensorType type,
+                      SensorReadingSharedBuffer* reading_buffer,
+                      PlatformSensorProvider* provider,
+                      const SensorInfoLinux* sensor_device);
 
   mojom::ReportingMode GetReportingMode() override;
 
@@ -45,8 +39,6 @@
   const PlatformSensorConfiguration default_configuration_;
   const mojom::ReportingMode reporting_mode_;
 
-  scoped_refptr<base::SingleThreadTaskRunner> polling_thread_task_runner_;
-
   // A sensor reader that reads values from sensor files
   // and stores them to a SensorReading structure.
   std::unique_ptr<SensorReader> sensor_reader_;
diff --git a/services/device/generic_sensor/platform_sensor_provider_linux.cc b/services/device/generic_sensor/platform_sensor_provider_linux.cc
index b25dd98..87ea250 100644
--- a/services/device/generic_sensor/platform_sensor_provider_linux.cc
+++ b/services/device/generic_sensor/platform_sensor_provider_linux.cc
@@ -10,9 +10,7 @@
 #include "base/bind.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/singleton.h"
-#include "base/message_loop/message_loop.h"
 #include "base/task_runner_util.h"
-#include "base/threading/thread.h"
 #include "services/device/generic_sensor/absolute_orientation_euler_angles_fusion_algorithm_using_accelerometer_and_magnetometer.h"
 #include "services/device/generic_sensor/linear_acceleration_fusion_algorithm_using_accelerometer.h"
 #include "services/device/generic_sensor/linux/sensor_data_linux.h"
@@ -94,14 +92,8 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(sensor_device);
 
-  if (!StartPollingThread()) {
-    callback.Run(nullptr);
-    return;
-  }
-
   scoped_refptr<PlatformSensorLinux> sensor =
-      new PlatformSensorLinux(type, reading_buffer, this, sensor_device,
-                              polling_thread_->task_runner());
+      new PlatformSensorLinux(type, reading_buffer, this, sensor_device);
   callback.Run(sensor);
 }
 
@@ -114,31 +106,6 @@
 
 void PlatformSensorProviderLinux::FreeResources() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_task_runner_);
-  // When there are no sensors left, the polling thread must be stopped.
-  // Stop() can only be called on a different thread that allows I/O.
-  // Thus, browser's file thread is used for this purpose.
-  file_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&PlatformSensorProviderLinux::StopPollingThread,
-                                base::Unretained(this)));
-}
-
-bool PlatformSensorProviderLinux::StartPollingThread() {
-  if (!polling_thread_)
-    polling_thread_.reset(new base::Thread("Sensor polling thread"));
-
-  if (!polling_thread_->IsRunning()) {
-    return polling_thread_->StartWithOptions(
-        base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
-  }
-  return true;
-}
-
-void PlatformSensorProviderLinux::StopPollingThread() {
-  DCHECK(file_task_runner_);
-  DCHECK(file_task_runner_->BelongsToCurrentThread());
-  if (polling_thread_ && polling_thread_->IsRunning())
-    polling_thread_->Stop();
 }
 
 void PlatformSensorProviderLinux::Shutdown() {
@@ -211,9 +178,8 @@
   scoped_refptr<PlatformSensorLinux> sensor;
   SensorReadingSharedBuffer* reading_buffer =
       GetSensorReadingSharedBufferForType(type);
-  if (sensor_device && reading_buffer && StartPollingThread()) {
-    sensor = new PlatformSensorLinux(type, reading_buffer, this, sensor_device,
-                                     polling_thread_->task_runner());
+  if (sensor_device && reading_buffer) {
+    sensor = new PlatformSensorLinux(type, reading_buffer, this, sensor_device);
   }
   NotifySensorCreated(type, sensor);
 }
diff --git a/services/device/generic_sensor/platform_sensor_provider_linux.h b/services/device/generic_sensor/platform_sensor_provider_linux.h
index e308233..90f1d318 100644
--- a/services/device/generic_sensor/platform_sensor_provider_linux.h
+++ b/services/device/generic_sensor/platform_sensor_provider_linux.h
@@ -13,7 +13,6 @@
 namespace base {
 template <typename T>
 struct DefaultSingletonTraits;
-class Thread;
 }  // namespace base
 
 namespace device {
@@ -59,12 +58,6 @@
       const PlatformSensorProviderBase::CreateSensorCallback& callback,
       const SensorInfoLinux* sensor_device);
 
-  bool StartPollingThread();
-
-  // Stops a polling thread if there are no sensors left. Must be called on
-  // a different than the polling thread which allows I/O.
-  void StopPollingThread();
-
   // Shuts down a service that tracks events from iio subsystem.
   void Shutdown();
 
@@ -104,16 +97,12 @@
   // Stores all available sensor devices by type.
   SensorDeviceMap sensor_devices_by_type_;
 
-  // A thread that is used by sensor readers in case of polling strategy.
-  std::unique_ptr<base::Thread> polling_thread_;
-
   // This manager is being used to get |SensorInfoLinux|, which represents
   // all the information of a concrete sensor provided by OS.
   std::unique_ptr<SensorDeviceManager> sensor_device_manager_;
 
-  // Browser's file thread task runner passed from renderer. Used by this
-  // provider to stop a polling thread and passed to a manager that
-  // runs a linux device monitor service on this task runner.
+  // Browser's file thread task runner passed from renderer. Passed to a manager
+  // that runs a linux device monitor service on this task runner.
   scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(PlatformSensorProviderLinux);
diff --git a/services/device/generic_sensor/platform_sensor_reader_linux.cc b/services/device/generic_sensor/platform_sensor_reader_linux.cc
index d1ddd0e..726b7aa 100644
--- a/services/device/generic_sensor/platform_sensor_reader_linux.cc
+++ b/services/device/generic_sensor/platform_sensor_reader_linux.cc
@@ -4,14 +4,17 @@
 
 #include "services/device/generic_sensor/platform_sensor_reader_linux.h"
 
+#include <memory>
+
 #include "base/bind.h"
 #include "base/files/file_util.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
-#include "base/threading/thread_checker.h"
-#include "base/threading/thread_restrictions.h"
+#include "base/task/post_task.h"
 #include "base/timer/timer.h"
 #include "services/device/generic_sensor/linux/sensor_data_linux.h"
 #include "services/device/generic_sensor/platform_sensor_linux.h"
@@ -21,129 +24,212 @@
 
 class PollingSensorReader : public SensorReader {
  public:
-  PollingSensorReader(const SensorInfoLinux* sensor_device,
+  PollingSensorReader(const SensorInfoLinux& sensor_info,
                       base::WeakPtr<PlatformSensorLinux> sensor,
                       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
   ~PollingSensorReader() override;
 
-  // SensorReader implements:
+  // SensorReader overrides
   void StartFetchingData(
       const PlatformSensorConfiguration& configuration) override;
   void StopFetchingData() override;
 
  private:
+  // Helper class that performs the actual I/O. It must run on a
+  // SequencedTaskRunner that is properly configured for blocking I/O
+  // operations.
+  class BlockingTaskHelper final {
+   public:
+    BlockingTaskHelper(base::WeakPtr<PollingSensorReader> polling_sensor_reader,
+                       const SensorInfoLinux& sensor_info);
+    ~BlockingTaskHelper();
+
+    // Starts polling for data at a given |frequency|, and stops if there is an
+    // error while reading or parsing the data.
+    void StartPolling(double frequency);
+
+    // Stops polling for data and notifying PollingSensorReader.
+    void StopPolling();
+
+   private:
+    void StopWithError();
+
+    void PollForData();
+
+    // Repeating timer for data polling.
+    base::RepeatingTimer timer_;
+
+    // |polling_sensor_reader_| can only be checked for validity on
+    // |owner_task_runner_|'s sequence.
+    base::WeakPtr<PollingSensorReader> polling_sensor_reader_;
+
+    // Task runner belonging to PollingSensorReader. Calls to
+    // PollingSensorReader will be done via this task runner.
+    scoped_refptr<base::SequencedTaskRunner> owner_task_runner_;
+
+    // Sensor information we need in order to know where and how to read sensor
+    // data.
+    const SensorInfoLinux sensor_info_;
+
+    SEQUENCE_CHECKER(sequence_checker_);
+
+    DISALLOW_COPY_AND_ASSIGN(BlockingTaskHelper);
+  };
+
+  // Receives a reading from the platform sensor and forwards it to |sensor_|.
+  void OnReadingAvailable(SensorReading reading);
+
+  // Signals that an error occurred while trying to read from a platform sensor.
+  void OnReadingError();
+
   // Initializes a read timer.
   void InitializeTimer(const PlatformSensorConfiguration& configuration);
 
-  // Polls data and sends it to a |sensor_|.
-  void PollForData();
-
-  // Paths to sensor read files.
-  const std::vector<base::FilePath> sensor_file_paths_;
-
-  // Scaling value that are applied to raw data from sensors.
-  const double scaling_value_;
-
-  // Offset value.
-  const double offset_value_;
-
-  // Used to apply scalings and invert signs if needed.
-  const SensorPathsLinux::ReaderFunctor apply_scaling_func_;
-
-  // Repeating timer for data polling.
-  base::RepeatingTimer timer_;
-
   // In builds with DCHECK enabled, checks that methods of this class are
   // called on the right thread.
-  THREAD_CHECKER(thread_checker_);
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  std::unique_ptr<BlockingTaskHelper> blocking_task_helper_;
+
+  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_ =
+      base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+           base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
+
+  base::WeakPtrFactory<PollingSensorReader> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PollingSensorReader);
 };
 
-PollingSensorReader::PollingSensorReader(
-    const SensorInfoLinux* sensor_device,
-    base::WeakPtr<PlatformSensorLinux> sensor,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : SensorReader(sensor, std::move(task_runner)),
-      sensor_file_paths_(sensor_device->device_reading_files),
-      scaling_value_(sensor_device->device_scaling_value),
-      offset_value_(sensor_device->device_offset_value),
-      apply_scaling_func_(sensor_device->apply_scaling_func) {
-  DETACH_FROM_THREAD(thread_checker_);
+PollingSensorReader::BlockingTaskHelper::BlockingTaskHelper(
+    base::WeakPtr<PollingSensorReader> polling_sensor_reader,
+    const SensorInfoLinux& sensor_info)
+    : polling_sensor_reader_(polling_sensor_reader),
+      owner_task_runner_(base::SequencedTaskRunnerHandle::Get()),
+      sensor_info_(sensor_info) {
+  // Detaches from the sequence on which this object was created. It will be
+  // bound to its owning sequence when StartPolling() is called.
+  DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
-PollingSensorReader::~PollingSensorReader() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+PollingSensorReader::BlockingTaskHelper::~BlockingTaskHelper() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-void PollingSensorReader::StartFetchingData(
-    const PlatformSensorConfiguration& configuration) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (is_reading_active_)
-    StopFetchingData();
-  InitializeTimer(configuration);
+void PollingSensorReader::BlockingTaskHelper::StartPolling(double frequency) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!timer_.IsRunning());
+  timer_.Start(FROM_HERE,
+               base::TimeDelta::FromMicroseconds(
+                   base::Time::kMicrosecondsPerSecond / frequency),
+               this, &PollingSensorReader::BlockingTaskHelper::PollForData);
 }
 
-void PollingSensorReader::StopFetchingData() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  is_reading_active_ = false;
+void PollingSensorReader::BlockingTaskHelper::StopPolling() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   timer_.Stop();
 }
 
-void PollingSensorReader::InitializeTimer(
-    const PlatformSensorConfiguration& configuration) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(!is_reading_active_);
-  timer_.Start(
-      FROM_HERE,
-      base::TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerSecond /
-                                        configuration.frequency()),
-      this, &PollingSensorReader::PollForData);
-  is_reading_active_ = true;
+void PollingSensorReader::BlockingTaskHelper::StopWithError() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  StopPolling();
+  owner_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&PollingSensorReader::OnReadingError,
+                                polling_sensor_reader_));
 }
 
-void PollingSensorReader::PollForData() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+void PollingSensorReader::BlockingTaskHelper::PollForData() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   SensorReading readings;
-  DCHECK_LE(sensor_file_paths_.size(), base::size(readings.raw.values));
+  DCHECK_LE(sensor_info_.device_reading_files.size(),
+            base::size(readings.raw.values));
   int i = 0;
-  for (const auto& path : sensor_file_paths_) {
+  for (const auto& path : sensor_info_.device_reading_files) {
     std::string new_read_value;
     if (!base::ReadFileToString(path, &new_read_value)) {
-      NotifyReadError();
-      StopFetchingData();
+      StopWithError();
       return;
     }
 
     double new_value = 0;
     base::TrimWhitespaceASCII(new_read_value, base::TRIM_ALL, &new_read_value);
     if (!base::StringToDouble(new_read_value, &new_value)) {
-      NotifyReadError();
-      StopFetchingData();
+      StopWithError();
       return;
     }
     readings.raw.values[i++] = new_value;
   }
-  if (!apply_scaling_func_.is_null())
-    apply_scaling_func_.Run(scaling_value_, offset_value_, readings);
 
-  if (is_reading_active_) {
-    task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&PlatformSensorLinux::UpdatePlatformSensorReading,
-                       sensor_, readings));
+  const auto& scaling_function = sensor_info_.apply_scaling_func;
+  if (!scaling_function.is_null()) {
+    scaling_function.Run(sensor_info_.device_scaling_value,
+                         sensor_info_.device_offset_value, readings);
   }
+
+  owner_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&PollingSensorReader::OnReadingAvailable,
+                                polling_sensor_reader_, readings));
+}
+
+PollingSensorReader::PollingSensorReader(
+    const SensorInfoLinux& sensor_info,
+    base::WeakPtr<PlatformSensorLinux> sensor,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : SensorReader(sensor, std::move(task_runner)), weak_factory_(this) {
+  blocking_task_helper_ = std::make_unique<BlockingTaskHelper>(
+      weak_factory_.GetWeakPtr(), sensor_info);
+}
+
+PollingSensorReader::~PollingSensorReader() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  blocking_task_runner_->DeleteSoon(FROM_HERE,
+                                    std::move(blocking_task_helper_));
+}
+
+void PollingSensorReader::StartFetchingData(
+    const PlatformSensorConfiguration& configuration) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (is_reading_active_)
+    StopFetchingData();
+  is_reading_active_ = true;
+  blocking_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&BlockingTaskHelper::StartPolling,
+                                base::Unretained(blocking_task_helper_.get()),
+                                configuration.frequency()));
+}
+
+void PollingSensorReader::StopFetchingData() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  is_reading_active_ = false;
+  blocking_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&BlockingTaskHelper::StopPolling,
+                                base::Unretained(blocking_task_helper_.get())));
+}
+
+void PollingSensorReader::OnReadingAvailable(SensorReading reading) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!is_reading_active_)
+    return;
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PlatformSensorLinux::UpdatePlatformSensorReading, sensor_,
+                     reading));
+}
+
+void PollingSensorReader::OnReadingError() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NotifyReadError();
 }
 
 // static
 std::unique_ptr<SensorReader> SensorReader::Create(
-    const SensorInfoLinux* sensor_device,
+    const SensorInfoLinux& sensor_info,
     base::WeakPtr<PlatformSensorLinux> sensor,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
   // TODO(maksims): implement triggered reading. At the moment,
   // only polling read is supported.
-  return std::make_unique<PollingSensorReader>(sensor_device, sensor,
+  return std::make_unique<PollingSensorReader>(sensor_info, sensor,
                                                std::move(task_runner));
 }
 
diff --git a/services/device/generic_sensor/platform_sensor_reader_linux.h b/services/device/generic_sensor/platform_sensor_reader_linux.h
index aed51dd4..1b88eca3 100644
--- a/services/device/generic_sensor/platform_sensor_reader_linux.h
+++ b/services/device/generic_sensor/platform_sensor_reader_linux.h
@@ -27,7 +27,7 @@
   // Creates a new instance of SensorReader. At the moment, only polling
   // reader is supported.
   static std::unique_ptr<SensorReader> Create(
-      const SensorInfoLinux* sensor_device,
+      const SensorInfoLinux& sensor_info,
       base::WeakPtr<PlatformSensorLinux> sensor,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
diff --git a/services/resource_coordinator/public/mojom/BUILD.gn b/services/resource_coordinator/public/mojom/BUILD.gn
index 8211d8d..2b2c4db 100644
--- a/services/resource_coordinator/public/mojom/BUILD.gn
+++ b/services/resource_coordinator/public/mojom/BUILD.gn
@@ -11,7 +11,6 @@
   sources = [
     "coordination_unit.mojom",
     "coordination_unit_introspector.mojom",
-    "coordination_unit_provider.mojom",
     "lifecycle.mojom",
     "memory_instrumentation/constants.mojom",
     "memory_instrumentation/memory_instrumentation.mojom",
diff --git a/services/resource_coordinator/public/mojom/coordination_unit.mojom b/services/resource_coordinator/public/mojom/coordination_unit.mojom
index 7fbd8dd..a594ac41 100644
--- a/services/resource_coordinator/public/mojom/coordination_unit.mojom
+++ b/services/resource_coordinator/public/mojom/coordination_unit.mojom
@@ -51,44 +51,10 @@
   int64 id;
 };
 
-struct ProcessResourceMeasurement {
-  // Identifies the process associated with this measurement.
-  mojo_base.mojom.ProcessId pid;
-
-  // The cumulative CPU usage accrued to this process from its start.
-  mojo_base.mojom.TimeDelta cpu_usage;
-
-  // The private memory footprint of the process.
-  uint32 private_footprint_kb = 0;
-};
-
-struct ProcessResourceMeasurementBatch {
-  // These times bracket the capture of the entire dump, e.g. each distinct
-  // measurement is captured somewhere between |batch_started_time| and
-  // |batch_ended_time|.
-  mojo_base.mojom.TimeTicks batch_started_time;
-  mojo_base.mojom.TimeTicks batch_ended_time;
-
-  array<ProcessResourceMeasurement> measurements;
-};
-
 // A FrameCoordinationUnit has at most one ProcessCoordinationUnit as its
 // parent, at most one PageCoordinationUnit as its parent, at most one
 // FrameCoordinationUnit as parent frame, and can have many child frames.
 interface FrameCoordinationUnit {
-  // Mainly used to force a round-trip to the service over the pipe for
-  // a specific unit, so we don't have to deal with possibly-not-yet-created
-  // children.
-  GetID() => (CoordinationUnitID id);
-
-  // Sets the process hosting this frame.
-  SetProcess(CoordinationUnitID id);
-
-  // Add a new binding to an existing FrameCoordinationUnit.
-  AddBinding(FrameCoordinationUnit& request);
-  AddChildFrame(CoordinationUnitID cu_id);
-  RemoveChildFrame(CoordinationUnitID cu_id);
-
   // Property signals.
   SetNetworkAlmostIdle(bool idle);
   SetLifecycleState(LifecycleState state);
@@ -100,51 +66,10 @@
   OnNonPersistentNotificationCreated();
 };
 
-interface PageCoordinationUnit {
-  // Mainly used to force a round-trip to the service over the pipe for
-  // a specific unit, so we don't have to deal with possibly-not-yet-created
-  // children.
-  GetID() => (CoordinationUnitID id);
-
-  // Add a new binding to an existing PageCoordinationUnit.
-  AddBinding(PageCoordinationUnit& request);
-  AddFrame(CoordinationUnitID cu_id);
-  RemoveFrame(CoordinationUnitID cu_id);
-
-  // Property signals.
-  SetIsLoading(bool is_loading);
-  SetVisibility(bool visible);
-  SetUKMSourceId(int64 ukm_source_id);
-
-  // Event signals.
-  OnFaviconUpdated();
-  OnTitleUpdated();
-
-  // |navigation_committed_time| is the time when the commit occurred.
-  // |navigation_id| is the unique ID of the navigation handle that was
-  // committed.
-  OnMainFrameNavigationCommitted(
-      mojo_base.mojom.TimeTicks navigation_committed_time,
-      int64 navigation_id, string url);
-};
-
 interface ProcessCoordinationUnit {
-  // Mainly used to force a round-trip to the service over the pipe for
-  // a specific unit, so we don't have to deal with possibly-not-yet-created
-  // children.
-  GetID() => (CoordinationUnitID id);
-
-  // Add a new binding to an existing ProcessCoordinationUnit.
-  AddBinding(ProcessCoordinationUnit& request);
-
   // Property signals.
-  SetCPUUsage(double cpu_usage);
   SetExpectedTaskQueueingDuration(mojo_base.mojom.TimeDelta duration);
-  SetLaunchTime(mojo_base.mojom.Time launch_time);
   SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low);
-  SetPID(mojo_base.mojom.ProcessId pid);
-  // Called when the process is known to have died or to have failed to start.
-  SetProcessExitStatus(int32 exit_status);
 
   // Event signals.
 
@@ -152,23 +77,3 @@
   // If nothing is done, then the renderer is likely to crash with OOM.
   OnRendererIsBloated();
 };
-
-// There is exactly one SystemCoordinationUnit at all times.
-interface SystemCoordinationUnit {
-  // Gets the ID of this coordination unit.
-  GetID() => (CoordinationUnitID id);
-
-  // Add a new binding to an existing SystemCoordinationUnit.
-  AddBinding(SystemCoordinationUnit& request);
-
-  // Event signals.
-
-  // Fired when all ProcessCUs have been adorned with consistent CPU usage
-  // metrics. This allows for subsequent heuristics to propagate these
-  // measurements to PageCUs via a heuristic.
-  OnProcessCPUUsageReady();
-
-  // Distributes a measurement batch onto the CU graph. This may generate
-  // events.
-  DistributeMeasurementBatch(ProcessResourceMeasurementBatch measurement_batch);
-};
diff --git a/services/resource_coordinator/public/mojom/coordination_unit_provider.mojom b/services/resource_coordinator/public/mojom/coordination_unit_provider.mojom
deleted file mode 100644
index 5bf29ea..0000000
--- a/services/resource_coordinator/public/mojom/coordination_unit_provider.mojom
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module resource_coordinator.mojom;
-
-import "services/resource_coordinator/public/mojom/coordination_unit.mojom";
-
-interface CoordinationUnitProvider {
-  // CU factory functions.
-  CreateFrameCoordinationUnit(FrameCoordinationUnit& request, CoordinationUnitID id);
-  CreatePageCoordinationUnit(PageCoordinationUnit& request, CoordinationUnitID id);
-  CreateProcessCoordinationUnit(ProcessCoordinationUnit& request, CoordinationUnitID id);
-
-  // Retrieves the always existing singleton SystemCU.
-  GetSystemCoordinationUnit(SystemCoordinationUnit& request);
-};
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index 8314b88..101ea97 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -1394,7 +1394,8 @@
               "cpu": "x86-64",
               "os": "Windows-10-15063"
             }
-          ]
+          ],
+          "shards": 4
         },
         "test": "net_unittests"
       },
@@ -2507,7 +2508,8 @@
               "cpu": "x86-64",
               "os": "Windows-10-15063"
             }
-          ]
+          ],
+          "shards": 4
         },
         "test": "net_unittests"
       },
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 171429d..0ae6dccb 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -13,7 +13,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -30,7 +30,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -48,7 +48,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -65,7 +65,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -82,7 +82,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -100,7 +100,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -119,7 +119,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -137,7 +137,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -156,7 +156,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -175,7 +175,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -194,7 +194,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -222,7 +222,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -249,7 +249,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -273,7 +273,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -299,7 +299,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -326,7 +326,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -353,7 +353,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -378,7 +378,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -403,7 +403,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -428,7 +428,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -453,7 +453,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -476,7 +476,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -493,7 +493,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -511,7 +511,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -528,7 +528,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -545,7 +545,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -563,7 +563,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -582,7 +582,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -600,7 +600,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -619,7 +619,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -638,7 +638,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -657,7 +657,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -676,7 +676,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -704,7 +704,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -731,7 +731,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -760,7 +760,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -783,7 +783,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -812,7 +812,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -837,7 +837,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -864,7 +864,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -889,7 +889,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -914,7 +914,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -939,7 +939,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -964,7 +964,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -989,7 +989,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -4754,7 +4754,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -4776,7 +4776,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -4787,7 +4787,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -4874,7 +4874,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -4896,7 +4896,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -4932,7 +4932,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -4943,7 +4943,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -4961,7 +4961,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -4971,7 +4971,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -4987,7 +4987,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -4997,7 +4997,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5013,7 +5013,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5024,7 +5024,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5040,7 +5040,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5050,7 +5050,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5066,7 +5066,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5076,7 +5076,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5091,7 +5091,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5101,7 +5101,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5113,7 +5113,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5123,7 +5123,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5148,7 +5148,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5158,7 +5158,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5181,7 +5181,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5191,7 +5191,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5214,7 +5214,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5224,7 +5224,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5247,7 +5247,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5257,7 +5257,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5284,7 +5284,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5294,7 +5294,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5324,7 +5324,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5334,7 +5334,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5372,7 +5372,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5382,7 +5382,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5406,7 +5406,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5416,7 +5416,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5439,7 +5439,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5449,7 +5449,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5472,7 +5472,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5483,7 +5483,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -5506,7 +5506,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -5517,7 +5517,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -6581,7 +6581,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -6592,7 +6592,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -6610,7 +6610,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -6620,7 +6620,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -6636,7 +6636,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -6646,7 +6646,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -6662,7 +6662,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -6673,7 +6673,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -6689,7 +6689,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -6699,7 +6699,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -6715,7 +6715,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -6725,7 +6725,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -6740,7 +6740,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -6750,7 +6750,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -6762,7 +6762,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -6772,7 +6772,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -6799,7 +6799,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -6808,7 +6808,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -7318,7 +7318,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7338,7 +7338,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7355,7 +7355,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7376,7 +7376,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7405,7 +7405,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7422,7 +7422,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7440,7 +7440,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7457,7 +7457,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7473,7 +7473,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7486,7 +7486,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7512,7 +7512,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7536,7 +7536,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7560,7 +7560,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7584,7 +7584,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7612,7 +7612,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7643,7 +7643,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7691,7 +7691,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7731,7 +7731,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7756,7 +7756,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7780,7 +7780,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7806,7 +7806,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7833,7 +7833,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7858,7 +7858,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7883,7 +7883,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7906,7 +7906,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7917,7 +7917,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -7935,7 +7935,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7945,7 +7945,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -7961,7 +7961,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -7971,7 +7971,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -7991,7 +7991,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8001,7 +8001,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8029,7 +8029,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8039,7 +8039,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8055,7 +8055,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8066,7 +8066,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8082,7 +8082,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8092,7 +8092,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8108,7 +8108,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8118,7 +8118,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8133,7 +8133,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8143,7 +8143,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8155,7 +8155,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8165,7 +8165,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8192,7 +8192,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8201,7 +8201,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8224,7 +8224,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8234,7 +8234,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8257,7 +8257,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8267,7 +8267,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8290,7 +8290,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8300,7 +8300,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8323,7 +8323,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8333,7 +8333,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8360,7 +8360,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8370,7 +8370,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8400,7 +8400,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8410,7 +8410,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8457,7 +8457,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8468,7 +8468,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8506,7 +8506,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8516,7 +8516,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8540,7 +8540,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8550,7 +8550,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8573,7 +8573,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8583,7 +8583,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8608,7 +8608,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8619,7 +8619,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8644,7 +8644,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8655,7 +8655,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8678,7 +8678,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8689,7 +8689,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8712,7 +8712,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8723,7 +8723,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8745,7 +8745,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8769,7 +8769,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8780,7 +8780,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8798,7 +8798,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8809,7 +8809,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8827,7 +8827,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8838,7 +8838,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -8856,7 +8856,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -8867,7 +8867,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15656,7 +15656,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15674,7 +15674,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15703,7 +15703,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15720,7 +15720,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15737,7 +15737,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15750,7 +15750,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15780,7 +15780,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15828,7 +15828,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15853,7 +15853,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15879,7 +15879,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15904,7 +15904,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-17.1.4",
+              "gpu": "intel-hd-630-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15927,7 +15927,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15938,7 +15938,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15954,7 +15954,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -15964,7 +15964,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15992,7 +15992,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -16002,7 +16002,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -16018,7 +16018,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -16029,7 +16029,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -16044,7 +16044,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -16054,7 +16054,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -16066,7 +16066,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -16076,7 +16076,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -16103,7 +16103,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -16112,7 +16112,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -16139,7 +16139,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -16149,7 +16149,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -16196,7 +16196,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -16207,7 +16207,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -16230,7 +16230,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -16240,7 +16240,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -16265,7 +16265,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -16276,7 +16276,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -16299,7 +16299,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -16310,7 +16310,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -16855,7 +16855,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -16872,7 +16872,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -16889,7 +16889,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -16906,7 +16906,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -16925,7 +16925,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -16941,7 +16941,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -16959,7 +16959,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -16978,7 +16978,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -16991,7 +16991,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17004,7 +17004,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17023,7 +17023,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17037,7 +17037,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17065,7 +17065,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17092,7 +17092,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17116,7 +17116,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17140,7 +17140,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17166,7 +17166,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17191,7 +17191,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17216,7 +17216,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17241,7 +17241,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17266,7 +17266,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17289,7 +17289,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17300,7 +17300,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17315,7 +17315,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17325,7 +17325,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17341,7 +17341,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17351,7 +17351,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17367,7 +17367,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17378,7 +17378,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17395,7 +17395,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17405,7 +17405,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17420,7 +17420,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17430,7 +17430,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17447,7 +17447,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17457,7 +17457,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17475,7 +17475,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17485,7 +17485,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17497,7 +17497,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17507,7 +17507,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17519,7 +17519,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17529,7 +17529,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17547,7 +17547,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17557,7 +17557,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17575,7 +17575,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17585,7 +17585,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17598,7 +17598,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17608,7 +17608,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17635,7 +17635,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17644,7 +17644,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17671,7 +17671,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17681,7 +17681,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17709,7 +17709,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17718,7 +17718,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17741,7 +17741,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17751,7 +17751,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17779,7 +17779,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17788,7 +17788,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17813,7 +17813,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17824,7 +17824,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17847,7 +17847,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17858,7 +17858,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17881,7 +17881,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17892,7 +17892,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17915,7 +17915,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17926,7 +17926,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17949,7 +17949,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17960,7 +17960,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -17983,7 +17983,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -17994,7 +17994,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -18015,7 +18015,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18032,7 +18032,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18050,7 +18050,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18067,7 +18067,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18084,7 +18084,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18102,7 +18102,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18120,7 +18120,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18136,7 +18136,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18152,7 +18152,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18170,7 +18170,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18189,7 +18189,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18202,7 +18202,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18215,7 +18215,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18234,7 +18234,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18253,7 +18253,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18272,7 +18272,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18286,7 +18286,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18312,7 +18312,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18336,7 +18336,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18360,7 +18360,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18384,7 +18384,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18412,7 +18412,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18443,7 +18443,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18482,7 +18482,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18507,7 +18507,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18531,7 +18531,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18555,7 +18555,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18580,7 +18580,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18605,7 +18605,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18630,7 +18630,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -18655,7 +18655,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19511,7 +19511,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19528,7 +19528,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19546,7 +19546,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19563,7 +19563,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19583,7 +19583,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19600,7 +19600,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19618,7 +19618,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19636,7 +19636,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19652,7 +19652,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19668,7 +19668,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19686,7 +19686,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19705,7 +19705,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19718,7 +19718,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19731,7 +19731,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19750,7 +19750,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19769,7 +19769,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19783,7 +19783,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19811,7 +19811,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19834,7 +19834,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19858,7 +19858,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19882,7 +19882,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19906,7 +19906,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19934,7 +19934,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -19965,7 +19965,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20004,7 +20004,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20028,7 +20028,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20053,7 +20053,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20077,7 +20077,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20103,7 +20103,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20130,7 +20130,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20157,7 +20157,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20182,7 +20182,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20207,7 +20207,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20232,7 +20232,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20257,7 +20257,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20280,7 +20280,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20291,7 +20291,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20306,7 +20306,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20316,7 +20316,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20333,7 +20333,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20343,7 +20343,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20359,7 +20359,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20369,7 +20369,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20388,7 +20388,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20398,7 +20398,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20414,7 +20414,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20425,7 +20425,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20441,7 +20441,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20451,7 +20451,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20468,7 +20468,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20478,7 +20478,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20493,7 +20493,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20503,7 +20503,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20518,7 +20518,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20528,7 +20528,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20545,7 +20545,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20555,7 +20555,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20573,7 +20573,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20583,7 +20583,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20595,7 +20595,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20605,7 +20605,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20617,7 +20617,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20627,7 +20627,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20645,7 +20645,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20655,7 +20655,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20673,7 +20673,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20683,7 +20683,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20701,7 +20701,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20711,7 +20711,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20724,7 +20724,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20734,7 +20734,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20761,7 +20761,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20770,7 +20770,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20793,7 +20793,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20803,7 +20803,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20826,7 +20826,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20836,7 +20836,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20859,7 +20859,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20869,7 +20869,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20892,7 +20892,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20902,7 +20902,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20929,7 +20929,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20939,7 +20939,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20969,7 +20969,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -20979,7 +20979,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21007,7 +21007,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21016,7 +21016,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21054,7 +21054,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21064,7 +21064,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21088,7 +21088,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21098,7 +21098,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21121,7 +21121,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21131,7 +21131,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21159,7 +21159,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21168,7 +21168,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21193,7 +21193,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21204,7 +21204,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21229,7 +21229,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21240,7 +21240,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21263,7 +21263,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21274,7 +21274,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21297,7 +21297,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21308,7 +21308,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21331,7 +21331,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21342,7 +21342,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21365,7 +21365,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21376,7 +21376,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21399,7 +21399,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21410,7 +21410,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21432,7 +21432,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "8086:5912-24.20.100.6286",
+              "gpu": "intel-hd-630-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21456,7 +21456,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21467,7 +21467,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21485,7 +21485,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21496,7 +21496,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21514,7 +21514,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21525,7 +21525,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21543,7 +21543,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21554,7 +21554,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21572,7 +21572,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21583,7 +21583,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21601,7 +21601,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21612,7 +21612,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21630,7 +21630,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21641,7 +21641,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21659,7 +21659,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21670,7 +21670,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21688,7 +21688,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21699,7 +21699,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21717,7 +21717,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -21728,7 +21728,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
diff --git a/testing/buildbot/chromium.gpu.json b/testing/buildbot/chromium.gpu.json
index 522546c5..53edb0da 100644
--- a/testing/buildbot/chromium.gpu.json
+++ b/testing/buildbot/chromium.gpu.json
@@ -293,7 +293,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -303,7 +303,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -319,7 +319,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -329,7 +329,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -345,7 +345,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -355,7 +355,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -380,7 +380,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -390,7 +390,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -413,7 +413,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -423,7 +423,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -446,7 +446,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -456,7 +456,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -479,7 +479,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -489,7 +489,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -516,7 +516,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -526,7 +526,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -556,7 +556,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -566,7 +566,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -604,7 +604,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -614,7 +614,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -638,7 +638,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -648,7 +648,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -671,7 +671,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -681,7 +681,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -704,7 +704,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -715,7 +715,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -737,7 +737,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -747,7 +747,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -767,7 +767,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -777,7 +777,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -793,7 +793,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -803,7 +803,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -819,7 +819,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -829,7 +829,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -854,7 +854,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -864,7 +864,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -887,7 +887,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -897,7 +897,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -920,7 +920,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -930,7 +930,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -953,7 +953,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -963,7 +963,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -990,7 +990,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -1000,7 +1000,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -1030,7 +1030,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -1040,7 +1040,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -1078,7 +1078,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -1088,7 +1088,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -1112,7 +1112,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -1122,7 +1122,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -1145,7 +1145,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -1155,7 +1155,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -1178,7 +1178,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -1189,7 +1189,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -2672,7 +2672,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2689,7 +2689,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2707,7 +2707,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2723,7 +2723,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2742,7 +2742,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2756,7 +2756,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2782,7 +2782,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2806,7 +2806,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2830,7 +2830,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2854,7 +2854,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2882,7 +2882,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2913,7 +2913,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2952,7 +2952,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -2977,7 +2977,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3001,7 +3001,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3025,7 +3025,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3048,7 +3048,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3058,7 +3058,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3077,7 +3077,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3087,7 +3087,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3103,7 +3103,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3113,7 +3113,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3130,7 +3130,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3140,7 +3140,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3155,7 +3155,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3165,7 +3165,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3183,7 +3183,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3193,7 +3193,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3206,7 +3206,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3216,7 +3216,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3241,7 +3241,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3251,7 +3251,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3274,7 +3274,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3284,7 +3284,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3307,7 +3307,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3317,7 +3317,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3340,7 +3340,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3350,7 +3350,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3377,7 +3377,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3387,7 +3387,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3417,7 +3417,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3427,7 +3427,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3465,7 +3465,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3475,7 +3475,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3499,7 +3499,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3509,7 +3509,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3532,7 +3532,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3542,7 +3542,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3565,7 +3565,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -3576,7 +3576,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-win10-stable\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 88d9d2e..b7f3c1d 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -10271,7 +10271,8 @@
             {
               "os": "Windows-10-15063"
             }
-          ]
+          ],
+          "shards": 4
         },
         "test": "net_unittests"
       },
diff --git a/testing/buildbot/client.v8.fyi.json b/testing/buildbot/client.v8.fyi.json
index 261eaa4..869bacb 100644
--- a/testing/buildbot/client.v8.fyi.json
+++ b/testing/buildbot/client.v8.fyi.json
@@ -290,7 +290,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -300,7 +300,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -323,7 +323,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -333,7 +333,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -356,7 +356,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -366,7 +366,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -389,7 +389,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -399,7 +399,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -429,7 +429,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -439,7 +439,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -477,7 +477,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -487,7 +487,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -511,7 +511,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -521,7 +521,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -544,7 +544,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -554,7 +554,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -579,7 +579,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -590,7 +590,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -613,7 +613,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -624,7 +624,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -651,7 +651,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -661,7 +661,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -684,7 +684,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -694,7 +694,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -717,7 +717,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -727,7 +727,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -750,7 +750,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -760,7 +760,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -790,7 +790,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -800,7 +800,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -838,7 +838,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -848,7 +848,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -872,7 +872,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -882,7 +882,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -905,7 +905,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -915,7 +915,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -940,7 +940,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -951,7 +951,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -974,7 +974,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-384.90",
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
@@ -985,7 +985,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-384.90\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"nvidia-quadro-p400-ubuntu-stable\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-410.78\", \"os\": \"Ubuntu\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -1374,7 +1374,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -1398,7 +1398,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -1422,7 +1422,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -1446,7 +1446,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -1477,7 +1477,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -1516,7 +1516,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -1541,7 +1541,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -1565,7 +1565,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -1591,7 +1591,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
@@ -1616,7 +1616,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-24.21.14.1195",
+              "gpu": "nvidia-quadro-p400-win10-stable",
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
diff --git a/testing/buildbot/filters/webui_polymer1_browser_tests.filter b/testing/buildbot/filters/webui_polymer1_browser_tests.filter
index c45803ec..d306723 100644
--- a/testing/buildbot/filters/webui_polymer1_browser_tests.filter
+++ b/testing/buildbot/filters/webui_polymer1_browser_tests.filter
@@ -221,6 +221,7 @@
 LoadTimeDataTest.*
 LocalAndDriveFileSystemExtensionApiTest.*
 LocalFileSystemExtensionApiTest.*
+MdSetTimeBrowserTest.*
 MediaEngagementWebUIBrowserTest.*
 MediaRouterElementsBrowserTest.*
 Mock4JSWebUITest.*
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index 6600efde..567882e 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -590,7 +590,13 @@
     if 'gpu' in dimension_set:
       # First remove the driver version, then split into vendor and device.
       gpu = dimension_set['gpu']
-      gpu = gpu.split('-')[0].split(':')
+      # Handle certain specialized named GPUs.
+      if gpu.startswith('nvidia-quadro-p400'):
+        gpu = ['10de', '1cb3']
+      elif gpu.startswith('intel-hd-630'):
+        gpu = ['8086', '5912']
+      else:
+        gpu = gpu.split('-')[0].split(':')
       substitutions['gpu_vendor_id'] = gpu[0]
       substitutions['gpu_device_id'] = gpu[1]
     return [string.Template(arg).safe_substitute(substitutions) for arg in args]
diff --git a/testing/buildbot/generate_buildbot_json_unittest.py b/testing/buildbot/generate_buildbot_json_unittest.py
index 942cacbe..5590d20 100755
--- a/testing/buildbot/generate_buildbot_json_unittest.py
+++ b/testing/buildbot/generate_buildbot_json_unittest.py
@@ -311,6 +311,54 @@
 ]
 """
 
+NVIDIA_GPU_TELEMETRY_TEST_WATERFALL = """\
+[
+  {
+    'name': 'chromium.test',
+    'machines': {
+      'Fake Tester': {
+        'os_type': 'win',
+        'browser_config': 'release',
+        'swarming': {
+          'dimension_sets': [
+            {
+              'gpu': 'nvidia-quadro-p400-win10-stable',
+            },
+          ],
+        },
+        'test_suites': {
+          'gpu_telemetry_tests': 'composition_tests',
+        },
+      },
+    },
+  },
+]
+"""
+
+INTEL_GPU_TELEMETRY_TEST_WATERFALL = """\
+[
+  {
+    'name': 'chromium.test',
+    'machines': {
+      'Fake Tester': {
+        'os_type': 'win',
+        'browser_config': 'release',
+        'swarming': {
+          'dimension_sets': [
+            {
+              'gpu': 'intel-hd-630-win10-stable',
+            },
+          ],
+        },
+        'test_suites': {
+          'gpu_telemetry_tests': 'composition_tests',
+        },
+      },
+    },
+  },
+]
+"""
+
 UNKNOWN_TEST_SUITE_WATERFALL = """\
 [
   {
@@ -645,6 +693,32 @@
 }
 """
 
+COMPOSITION_SUITE_WITH_GPU_ARGS = """\
+{
+  'basic_suites': {
+    'foo_tests': {
+      'foo': {
+        'args': [
+          '--gpu-vendor-id',
+          '${gpu_vendor_id}',
+          '--gpu-device-id',
+          '${gpu_device_id}',
+        ],
+      },
+    },
+    'bar_tests': {
+      'bar_test': {},
+    },
+  },
+  'compound_suites': {
+    'composition_tests': [
+      'foo_tests',
+      'bar_tests',
+    ],
+  },
+}
+"""
+
 EMPTY_PYL_FILE = """\
 {
 }
@@ -1166,6 +1240,80 @@
 }
 """
 
+NVIDIA_GPU_TELEMETRY_TEST_OUTPUT = """\
+{
+  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
+  "AAAAA2 See generate_buildbot_json.py to make changes": {},
+  "Fake Tester": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "foo",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--gpu-vendor-id",
+          "10de",
+          "--gpu-device-id",
+          "1cb3"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "foo_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-win10-stable"
+            }
+          ],
+          "idempotent": false
+        }
+      }
+    ]
+  }
+}
+"""
+
+INTEL_GPU_TELEMETRY_TEST_OUTPUT = """\
+{
+  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
+  "AAAAA2 See generate_buildbot_json.py to make changes": {},
+  "Fake Tester": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "foo",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--gpu-vendor-id",
+          "8086",
+          "--gpu-device-id",
+          "5912"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "foo_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-win10-stable"
+            }
+          ],
+          "idempotent": false
+        }
+      }
+    ]
+  }
+}
+"""
+
 ANDROID_WATERFALL_OUTPUT = """\
 {
   "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
@@ -1864,6 +2012,26 @@
     fbb.check_output_file_consistency(verbose=True)
     self.assertFalse(fbb.printed_lines)
 
+  def test_nvidia_gpu_telemetry_tests(self):
+    fbb = FakeBBGen(NVIDIA_GPU_TELEMETRY_TEST_WATERFALL,
+                    COMPOSITION_SUITE_WITH_GPU_ARGS,
+                    NO_BAR_TEST_EXCEPTIONS,
+                    EMPTY_PYL_FILE,
+                    LUCI_MILO_CFG)
+    fbb.files['chromium.test.json'] = NVIDIA_GPU_TELEMETRY_TEST_OUTPUT
+    fbb.check_output_file_consistency(verbose=True)
+    self.assertFalse(fbb.printed_lines)
+
+  def test_intel_gpu_telemetry_tests(self):
+    fbb = FakeBBGen(INTEL_GPU_TELEMETRY_TEST_WATERFALL,
+                    COMPOSITION_SUITE_WITH_GPU_ARGS,
+                    NO_BAR_TEST_EXCEPTIONS,
+                    EMPTY_PYL_FILE,
+                    LUCI_MILO_CFG)
+    fbb.files['chromium.test.json'] = INTEL_GPU_TELEMETRY_TEST_OUTPUT
+    fbb.check_output_file_consistency(verbose=True)
+    self.assertFalse(fbb.printed_lines)
+
   def test_instrumentation_tests_with_different_names(self):
     fbb = FakeBBGen(FOO_INSTRUMENTATION_TEST_WATERFALL,
                     INSTRUMENTATION_TESTS_WITH_DIFFERENT_NAMES,
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index eb68f15..6bdb99d 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -249,7 +249,8 @@
   'linux_intel_hd_630': {
     'swarming': {
       'dimensions': {
-        'gpu': '8086:5912-17.1.4',
+        # Defined in bot_config.py in internal infradata/config workspace
+        'gpu': 'intel-hd-630-ubuntu-stable',
         'os': 'Ubuntu',
         'pool': 'Chrome-GPU',
       }
@@ -268,7 +269,8 @@
   'linux_nvidia_quadro_p400': {
     'swarming': {
       'dimensions': {
-        'gpu': '10de:1cb3-384.90',
+        # Defined in bot_config.py in internal infradata/config workspace
+        'gpu': 'nvidia-quadro-p400-ubuntu-stable',
         'os': 'Ubuntu',
         'pool': 'Chrome-GPU',
       }
@@ -509,7 +511,8 @@
   'win10_intel_hd_630_stable': {
     'swarming': {
       'dimensions': {
-        'gpu': '8086:5912-24.20.100.6286',
+        # Defined in bot_config.py in internal infradata/config workspace
+        'gpu': 'intel-hd-630-win10-stable',
         'os': 'Windows-10',
         'pool': 'Chrome-GPU',
       },
@@ -527,7 +530,8 @@
   'win10_nvidia_quadro_p400_stable': {
     'swarming': {
       'dimensions': {
-        'gpu': '10de:1cb3-24.21.14.1195',
+        # Defined in bot_config.py in internal infradata/config workspace
+        'gpu': 'nvidia-quadro-p400-win10-stable',
         'os': 'Windows-10',
         'pool': 'Chrome-GPU',
       },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index cc5ef61..c8a4c1a4 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -724,6 +724,16 @@
     ],
     'modifications': {
       # chromium.clang
+      'CrWinAsan': {
+        'swarming': {
+          'shards': 4,  # TODO(https://crbug.com/942881): debug, remove.
+        }
+      },
+      'CrWinAsan(dll)': {
+        'swarming': {
+          'shards': 4,  # TODO(https://crbug.com/942881): debug, remove.
+        }
+      },
       'ToTLinuxASan': {
         # TODO(crbug.com/794372): net_unittests is slow under ASan.
         'swarming': {
@@ -778,6 +788,11 @@
           'shards': 2,
         },
       },
+      'win-asan': {
+        'swarming': {
+          'shards': 4,  # TODO(https://crbug.com/942881): debug, remove.
+        },
+      },
     },
   },
   'network_service_browser_tests': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index d9f4f99..fae85f31 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3338,11 +3338,12 @@
             ],
             "experiments": [
                 {
-                    "name": "Intervention_R9",
+                    "name": "Intervention_R12_12%_Threshold",
                     "params": {
                         "navigate_ads": "true",
                         "pause_renderer": "true",
-                        "renderer_workload_threshold_percentage": "16"
+                        "purge_v8": "true",
+                        "renderer_pmf_threshold_percentage": "12"
                     },
                     "enable_features": [
                         "OomIntervention"
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 8e44bec5..0672baa 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -124,12 +124,6 @@
 const base::Feature kRTCOfferExtmapAllowMixed{
     "RTCOfferExtmapAllowMixed", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables to load the response body through Mojo data pipe passed by
-// WebURLLoaderClient::DidStartLoadingResponseBody() instead of
-// WebURLLoaderClient::DidReceiveData().
-const base::Feature kResourceLoadViaDataPipe{"ResourceLoadViaDataPipe",
-                                             base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kServiceWorkerImportedScriptUpdateCheck{
     "ServiceWorkerImportedScriptUpdateCheck",
     base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index 050f7028..699b40d 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -31,12 +31,9 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_PLATFORM_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_PLATFORM_H_
 
-#ifdef WIN32
-#include <windows.h>
-#endif
-
 #include <memory>
 
+#include "base/files/file.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/string_piece.h"
@@ -144,14 +141,6 @@
 
 class BLINK_PLATFORM_EXPORT Platform {
  public:
-// HTML5 Database ------------------------------------------------------
-
-#ifdef WIN32
-  typedef HANDLE FileHandle;
-#else
-  typedef int FileHandle;
-#endif
-
   // Initialize platform and wtf. If you need to initialize the entire Blink,
   // you should use blink::Initialize. WebThreadScheduler must be owned by
   // the embedder.
@@ -229,12 +218,12 @@
   // Must return non-null.
   virtual WebBlobRegistry* GetBlobRegistry() { return nullptr; }
 
-  // Database ------------------------------------------------------------
+  // Database (WebSQL) ---------------------------------------------------
 
   // Opens a database file.
-  virtual FileHandle DatabaseOpenFile(const WebString& vfs_file_name,
+  virtual base::File DatabaseOpenFile(const WebString& vfs_file_name,
                                       int desired_flags) {
-    return FileHandle();
+    return base::File();
   }
 
   // Deletes a database file and returns the error code.
diff --git a/third_party/blink/public/web/web_view_client.h b/third_party/blink/public/web/web_view_client.h
index 5b0bc98..c435c4c 100644
--- a/third_party/blink/public/web/web_view_client.h
+++ b/third_party/blink/public/web/web_view_client.h
@@ -65,7 +65,7 @@
   // The request parameter is only for the client to check if the request
   // could be fulfilled.  The client should not load the request.
   // The policy parameter indicates how the new view will be displayed in
-  // WebWidgetClient::show.
+  // WebWidgetClient::Show.
   virtual WebView* CreateView(
       WebLocalFrame* creator,
       const WebURLRequest& request,
diff --git a/third_party/blink/renderer/bindings/core/v8/BUILD.gn b/third_party/blink/renderer/bindings/core/v8/BUILD.gn
index 16a4e5a..40a266b 100644
--- a/third_party/blink/renderer/bindings/core/v8/BUILD.gn
+++ b/third_party/blink/renderer/bindings/core/v8/BUILD.gn
@@ -46,6 +46,8 @@
   "$bindings_core_v8_output_dir/event_listener_options_or_boolean.h",
   "$bindings_core_v8_output_dir/file_or_usv_string.cc",
   "$bindings_core_v8_output_dir/file_or_usv_string.h",
+  "$bindings_core_v8_output_dir/file_or_usv_string_or_form_data.cc",
+  "$bindings_core_v8_output_dir/file_or_usv_string_or_form_data.h",
   "$bindings_core_v8_output_dir/float_or_string_element_record.cc",
   "$bindings_core_v8_output_dir/float_or_string_element_record.h",
   "$bindings_core_v8_output_dir/html_element_or_long.cc",
@@ -146,8 +148,8 @@
   "$bindings_core_v8_output_dir/v8_custom_element_disabled_state_changed_callback.h",
   "$bindings_core_v8_output_dir/v8_custom_element_form_associated_callback.cc",
   "$bindings_core_v8_output_dir/v8_custom_element_form_associated_callback.h",
-  "$bindings_core_v8_output_dir/v8_custom_element_restore_value_callback.cc",
-  "$bindings_core_v8_output_dir/v8_custom_element_restore_value_callback.h",
+  "$bindings_core_v8_output_dir/v8_custom_element_restore_state_callback.cc",
+  "$bindings_core_v8_output_dir/v8_custom_element_restore_state_callback.h",
   "$bindings_core_v8_output_dir/v8_event_handler_non_null.cc",
   "$bindings_core_v8_output_dir/v8_event_handler_non_null.h",
   "$bindings_core_v8_output_dir/v8_for_each_iterator_callback.cc",
diff --git a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc
index 828f6f7..6421753 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc
@@ -12,7 +12,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_disabled_state_changed_callback.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_form_associated_callback.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_registry.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_restore_value_callback.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_restore_state_callback.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_element.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_script_runner.h"
@@ -113,7 +113,7 @@
       form_associated_callback_(data.form_associated_callback_),
       form_reset_callback_(data.form_reset_callback_),
       disabled_state_changed_callback_(data.disabled_state_changed_callback_),
-      restore_value_callback_(data.restore_value_callback_) {}
+      restore_state_callback_(data.restore_state_callback_) {}
 
 void ScriptCustomElementDefinition::Trace(Visitor* visitor) {
   visitor->Trace(script_state_);
@@ -125,7 +125,7 @@
   visitor->Trace(form_associated_callback_);
   visitor->Trace(form_reset_callback_);
   visitor->Trace(disabled_state_changed_callback_);
-  visitor->Trace(restore_value_callback_);
+  visitor->Trace(restore_state_callback_);
   CustomElementDefinition::Trace(visitor);
 }
 
@@ -278,8 +278,8 @@
   return disabled_state_changed_callback_;
 }
 
-bool ScriptCustomElementDefinition::HasRestoreValueCallback() const {
-  return restore_value_callback_;
+bool ScriptCustomElementDefinition::HasRestoreStateCallback() const {
+  return restore_state_callback_;
 }
 
 void ScriptCustomElementDefinition::RunConnectedCallback(Element& element) {
@@ -340,13 +340,13 @@
                                                              is_disabled);
 }
 
-void ScriptCustomElementDefinition::RunRestoreValueCallback(
+void ScriptCustomElementDefinition::RunRestoreStateCallback(
     Element& element,
-    const FileOrUSVString& value,
+    const FileOrUSVStringOrFormData& value,
     const String& mode) {
-  if (!restore_value_callback_)
+  if (!restore_state_callback_)
     return;
-  restore_value_callback_->InvokeAndReportException(&element, value, mode);
+  restore_state_callback_->InvokeAndReportException(&element, value, mode);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.h b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.h
index 0f8da05..7fe8c4d 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.h
@@ -23,7 +23,7 @@
 class V8CustomElementConstructor;
 class V8CustomElementDisabledStateChangedCallback;
 class V8CustomElementFormAssociatedCallback;
-class V8CustomElementRestoreValueCallback;
+class V8CustomElementRestoreStateCallback;
 class V8VoidFunction;
 
 class CORE_EXPORT ScriptCustomElementDefinition final
@@ -56,7 +56,7 @@
   bool HasFormAssociatedCallback() const override;
   bool HasFormResetCallback() const override;
   bool HasDisabledStateChangedCallback() const override;
-  bool HasRestoreValueCallback() const override;
+  bool HasRestoreStateCallback() const override;
 
   void RunConnectedCallback(Element&) override;
   void RunDisconnectedCallback(Element&) override;
@@ -72,8 +72,8 @@
   void RunFormResetCallback(Element& element) override;
   void RunDisabledStateChangedCallback(Element& element,
                                        bool is_disabled) override;
-  void RunRestoreValueCallback(Element& element,
-                               const FileOrUSVString& value,
+  void RunRestoreStateCallback(Element& element,
+                               const FileOrUSVStringOrFormData& value,
                                const String& mode) override;
 
  private:
@@ -101,8 +101,8 @@
   TraceWrapperMember<V8VoidFunction> form_reset_callback_;
   TraceWrapperMember<V8CustomElementDisabledStateChangedCallback>
       disabled_state_changed_callback_;
-  TraceWrapperMember<V8CustomElementRestoreValueCallback>
-      restore_value_callback_;
+  TraceWrapperMember<V8CustomElementRestoreStateCallback>
+      restore_state_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(ScriptCustomElementDefinition);
 };
diff --git a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition_builder.cc b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition_builder.cc
index 55ec5d6..0b757f5 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition_builder.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition_builder.cc
@@ -14,7 +14,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_constructor.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_disabled_state_changed_callback.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_form_associated_callback.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_restore_value_callback.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_restore_state_callback.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_void_function.h"
 #include "third_party/blink/renderer/platform/bindings/callback_method_retriever.h"
 #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
@@ -204,14 +204,14 @@
               v8_disabled_state_changed_callback_.As<v8::Function>());
     }
 
-    v8_restore_value_callback_ = retriever.GetMethodOrUndefined(
-        "restoreValueCallback", exception_state_);
+    v8_restore_state_callback_ = retriever.GetMethodOrUndefined(
+        "restoreStateCallback", exception_state_);
     if (exception_state_.HadException())
       return false;
-    if (v8_restore_value_callback_->IsFunction()) {
-      data_.restore_value_callback_ =
-          V8CustomElementRestoreValueCallback::Create(
-              v8_restore_value_callback_.As<v8::Function>());
+    if (v8_restore_state_callback_->IsFunction()) {
+      data_.restore_state_callback_ =
+          V8CustomElementRestoreStateCallback::Create(
+              v8_restore_state_callback_.As<v8::Function>());
     }
   }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition_builder.h b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition_builder.h
index b042a6a..3861cae 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition_builder.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition_builder.h
@@ -50,7 +50,7 @@
   v8::Local<v8::Value> v8_form_associated_callback_;
   v8::Local<v8::Value> v8_form_reset_callback_;
   v8::Local<v8::Value> v8_disabled_state_changed_callback_;
-  v8::Local<v8::Value> v8_restore_value_callback_;
+  v8::Local<v8::Value> v8_restore_state_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(ScriptCustomElementDefinitionBuilder);
 };
diff --git a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition_data.h b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition_data.h
index 7efb089..772ff18 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition_data.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition_data.h
@@ -20,7 +20,7 @@
 class V8CustomElementConstructor;
 class V8CustomElementDisabledStateChangedCallback;
 class V8CustomElementFormAssociatedCallback;
-class V8CustomElementRestoreValueCallback;
+class V8CustomElementRestoreStateCallback;
 class V8VoidFunction;
 
 class ScriptCustomElementDefinitionData {
@@ -40,7 +40,7 @@
   Member<V8VoidFunction> form_reset_callback_;
   Member<V8CustomElementDisabledStateChangedCallback>
       disabled_state_changed_callback_;
-  Member<V8CustomElementRestoreValueCallback> restore_value_callback_;
+  Member<V8CustomElementRestoreStateCallback> restore_state_callback_;
   HashSet<AtomicString> observed_attributes_;
   Vector<String> disabled_features_;
   bool is_form_associated_ = false;
diff --git a/third_party/blink/renderer/bindings/modules/v8/BUILD.gn b/third_party/blink/renderer/bindings/modules/v8/BUILD.gn
index 8a0f890..0745a1c 100644
--- a/third_party/blink/renderer/bindings/modules/v8/BUILD.gn
+++ b/third_party/blink/renderer/bindings/modules/v8/BUILD.gn
@@ -124,6 +124,7 @@
     ":bindings_modules_origin_trial_features",
     ":bindings_modules_v8_generated",
     ":generate_mojo_bindings",
+    "//third_party/dawn/src/dawn:dawn_headers",
   ]
 
   if (!is_component_build && is_win) {
diff --git a/third_party/blink/renderer/bindings/scripts/overload_set_algorithm.py b/third_party/blink/renderer/bindings/scripts/overload_set_algorithm.py
index 6e819d5..351ca8e 100644
--- a/third_party/blink/renderer/bindings/scripts/overload_set_algorithm.py
+++ b/third_party/blink/renderer/bindings/scripts/overload_set_algorithm.py
@@ -18,26 +18,31 @@
     """Returns the effective overload set of an overloaded function.
 
     An effective overload set is the set of overloaded functions + signatures
-    (type list of arguments, with optional and variadic arguments included or
-    not), and is used in the overload resolution algorithm.
+    derived from the set F by transforming it into the distinct permutations of
+    function invocations possible given the argument types. It is used by the
+    overload resolution algorithm.
 
-    For example, given input [f1(optional long x), f2(DOMString s)], the output
-    is informally [f1(), f1(long), f2(DOMString)], and formally
-    [(f1, [], []), (f1, [long], [optional]), (f2, [DOMString], [required])].
+    For example, given input:
+    [
+        f1(DOMString a),
+        f2(Node a, DOMString b, double... c),
+        f3(),
+        f4(Event a, DOMString b, optional DOMString c, double... d)
+    ]
 
-    Currently the optionality list is a list of |is_optional| booleans (True
-    means optional, False means required); to support variadics this needs to
-    be tri-valued as required, optional, or variadic.
+    The output is:
+    [
+        (f1, [DOMString], [required]),
+        (f2, [Node, DOMString], [required, required]),
+        (f2, [Node, DOMString, double], [required, required, variadic]),
+        (f2, [Node, DOMString, double, double], [required, required, variadic, variadic]),
+        (f3, [], []),
+        (f4, [Event, DOMString], [required, required],
+        (f4, [Event, DOMString, DOMString], [required, required, optional]),
+        (f4, [Event, DOMString, DOMString, double], [required, required, optional, variadic])
+    ]
 
-    Formally:
-    An effective overload set represents the allowable invocations for a
-    particular operation, constructor (specified with [Constructor] or
-    [NamedConstructor]), or callback function.
-
-    An additional argument N (argument count) is needed when overloading
-    variadics, but we don't use that currently.
-
-    Spec: http://heycam.github.io/webidl/#dfn-effective-overload-set
+    Spec: http://heycam.github.io/webidl/#dfn-effective-overload-set.
 
     Formally the input and output lists are sets, but methods are stored
     internally as dicts, which can't be stored in a set because they are not
@@ -45,64 +50,121 @@
 
     Arguments:
         F: list of overloads for a given callable name.
-        value_reader: an OverloadSetValueReader instance.
 
     Returns:
-        S: list of tuples of the form (callable, type list, optionality list).
+        S: list of tuples of the form:
+        (callable, tuple(type list), tuple(optionality list)).
     """
     # Code closely follows the algorithm in the spec, for clarity and
     # correctness, and hence is not very Pythonic.
 
-    # 1. Initialize S to ∅.
+    # 1. Let S be an ordered set.
     # (We use a list because we can't use a set, as noted above.)
     S = []  # pylint: disable=invalid-name
 
-    # 2. Let F be a set with elements as follows, according to the kind of
+    # 2. Let F be an ordered set with items as follows, according to the kind of
     # effective overload set:
     # (Passed as argument, nothing to do.)
 
-    # 3. & 4. (maxarg, m) are only needed for variadics, not used.
+    # 3. Let maxarg be the maximum number of arguments the operations,
+    # constructor extended attributes or callback functions in F are declared
+    # to take. For variadic operations and constructor extended attributes,
+    # the argument on which the ellipsis appears counts as a single argument.
+    # Note: So void f(long x, long... y); is considered to be declared to take
+    # two arguments.
+    # X is the "callable".
+    maxarg = max([len(X['arguments']) for X in F])
+
+    # 4. Let max be max(maxarg, N).
+    # Per: https://github.com/heycam/webidl/issues/600.
+
+    # The effective overload set as defined in the Web IDL spec is used at
+    # runtime as an input to the overload resolution algorithm. The runtime
+    # portion of the overload resolution algorithm includes coercing arguments
+    # into the proper type. To perform that coercion, the effective overload set
+    # must produce variadic entries in the type list to match the number of
+    # arguments supplied for the invocation (N) of the function.
+
+    # Our use of the effective overload set, however, is limited to determining
+    # which function overload should handle the invocation. Coercion of
+    # arguments is a separate problem making N irrelevant and max always equal
+    # to maxarg.
 
     # 5. For each operation, extended attribute or callback function X in F:
-    for X in F:  # X is the "callable". pylint: disable=invalid-name
+    for X in F:  # pylint: disable=invalid-name
+        # 5.1. Let arguments be the list of arguments X is declared to take.
         arguments = X['arguments']  # pylint: disable=invalid-name
-        # 1. Let n be the number of arguments X is declared to take.
+        # 5.2. Let n be the size of arguments.
         n = len(arguments)  # pylint: disable=invalid-name
-        # 2. Let t0..n−1 be a list of types, where ti is the type of X’s
-        # argument at index i.
-        # (“type list”)
-        t = tuple(argument['idl_type_object']  # pylint: disable=invalid-name
-                  for argument in arguments)
-        # 3. Let o0..n−1 be a list of optionality values, where oi is “variadic”
-        # if X’s argument at index i is a final, variadic argument, “optional”
-        # if the argument is optional, and “required” otherwise.
-        # (“optionality list”)
-        # (We’re just using a boolean for optional/variadic vs. required.)
-        o = tuple(argument['is_optional']  # pylint: disable=invalid-name
-                  or argument['is_variadic']
-                  for argument in arguments)
-        # 4. Add to S the tuple <X, t0..n−1, o0..n−1>.
-        S.append((X, t, o))
-        # 5. If X is declared to be variadic, then:
-        # (Not used, so not implemented.)
-        # 6. Initialize i to n−1.
+        # 5.3. Let types be a type list.
+        # 5.4. Let optionalityValues be an optionality list.
+        types, optionality_values = [], []
+
+        # 5.5. For each argument in arguments:
+        for argument in arguments:
+            # 5.5.1. Append the type of argument to types.
+            types.append(argument['idl_type_object'])
+            # 5.5.2. Append "variadic" to optionalityValues if argument is a
+            # final, variadic argument, "optional" if argument is optional,
+            # and "required" otherwise.
+            if argument['is_variadic']:
+                optionality_values.append('variadic')
+            elif argument['is_optional']:
+                optionality_values.append('optional')
+            else:
+                optionality_values.append('required')
+
+        # 5.6. Append the tuple (X, types, optionalityValues) to S.
+        S.append((X, tuple(types), tuple(optionality_values)))
+
+        # 5.7. If X is declared to be variadic, then:
+        if optionality_values and optionality_values[-1] == 'variadic':
+            # 5.7.1. For each i in the range n to max - 1, inclusive:
+            for i in range(n, maxarg):
+                # 5.7.1.1. Let t be a type list.
+                # 5.7.1.2. Let o be an optionality list.
+                type_list, optionality_list = [], []
+                # 5.7.1.3. For each j in the range 0 to n-1, inclusive:
+                for j in range(0, n):
+                    # 5.7.1.3.1. Append types[j] to t.
+                    type_list.append(types[j])
+                    # 5.7.1.3.2. Append optionalityValues[j] to o.
+                    optionality_list.append(optionality_values[j])
+                # 5.7.1.4. For each j in the range n to i, inclusive:
+                for j in range(n, i + 1):
+                    # 5.7.1.4.1. Append types[n - 1] to t.
+                    type_list.append(types[n - 1])
+                    # 5.7.1.4.2. Append "variadic" to o.
+                    optionality_list.append('variadic')
+                # 5.7.1.5. Append the tuple (X, t, o) to S.
+                S.append((X, tuple(type_list), tuple(optionality_list)))
+        # 5.8. Let i be n - 1.
         i = n - 1
-        # 7. While i ≥ 0:
-        # Spec bug (fencepost error); should be “While i > 0:”
-        # https://www.w3.org/Bugs/Public/show_bug.cgi?id=25590
-        while i > 0:
-            # 1. If argument i of X is not optional, then break this loop.
-            if not o[i]:
+
+        # 5.9. While i ≥ 0:
+        while i >= 0:
+            # 5.9.1. If arguments[i] is not optional (i.e., it is not marked as
+            # "optional" and is not a final, variadic argument), then break.
+            if optionality_values[i] == 'required':
                 break
-            # 2. Otherwise, add to S the tuple <X, t0..i−1, o0..i−1>.
-            S.append((X, t[:i], o[:i]))
-            # 3. Set i to i−1.
+
+            # 5.9.2. Let t be a type list.
+            # 5.9.3. Let o be an optionality list.
+            type_list, optionality_list = [], []
+            # 5.9.4. For each j in the range 0 to i-1, inclusive:
+            for j in range(0, i):
+                # 5.9.4.1. Append types[j] to t.
+                type_list.append(types[j])
+                # 5.9.4.2. Append optionalityValues[j] to o.
+                optionality_list.append(optionality_values[j])
+            # 5.9.5. Append the tuple (X, t, o) to S.
+            # Note: if i is 0, this means to add to S the tuple (X, « », « »);
+            # (where "« »" represents an empty list).
+            S.append((X, tuple(type_list), tuple(optionality_list)))
+            # 5.9.6. Set i to i−1.
             i = i - 1
-        # 8. If n > 0 and all arguments of X are optional, then add to S the
-        # tuple <X, (), ()> (where “()” represents the empty list).
-        if n > 0 and all(oi for oi in o):
-            S.append((X, (), ()))
-    # 6. The effective overload set is S.
+
+    # 6. Return S.
     return S
 
 
diff --git a/third_party/blink/renderer/bindings/scripts/overload_set_algorithm_test.py b/third_party/blink/renderer/bindings/scripts/overload_set_algorithm_test.py
index 389a9ed..44ccd20 100644
--- a/third_party/blink/renderer/bindings/scripts/overload_set_algorithm_test.py
+++ b/third_party/blink/renderer/bindings/scripts/overload_set_algorithm_test.py
@@ -13,39 +13,41 @@
 class EffectiveOverloadSetTest(unittest.TestCase):
     def test_example_in_comments(self):
         operation_list = [
-            {'arguments': [{'idl_type_object': 'long',  # f1(optional long x)
+            # f1: f(optional long x)
+            {'arguments': [{'idl_type_object': 'long',
                             'is_optional': True,
                             'is_variadic': False}]},
-            {'arguments': [{'idl_type_object': 'DOMString',  # f2(DOMString s)
+            # f2: f(DOMString s)
+            {'arguments': [{'idl_type_object': 'DOMString',
                             'is_optional': False,
                             'is_variadic': False}]}]
 
         overload_set = [
-            ({'arguments': [{'idl_type_object': 'long',  # f1(long)
+            # <f1, (long), (optional)>
+            ({'arguments': [{'idl_type_object': 'long',
                              'is_optional': True,
                              'is_variadic': False}]},
              ('long',),
-             (True,)),
-            ({'arguments': [{'idl_type_object': 'long',  # f1()
+             ('optional',)),
+            # <f1, (), ()>
+            ({'arguments': [{'idl_type_object': 'long',
                              'is_optional': True,
                              'is_variadic': False}]},
              (),
              ()),
-            ({'arguments': [{'idl_type_object': 'DOMString',  # f2(DOMString)
+            # <f2, (DOMString), (required)>
+            ({'arguments': [{'idl_type_object': 'DOMString',
                              'is_optional': False,
                              'is_variadic': False}]},
              ('DOMString',),
-             (False,))]
+             ('required',))]
 
         self.assertEqual(effective_overload_set(operation_list), overload_set)
 
     def test_example_in_spec(self):
         """Tests the example provided in Web IDL spec:
            https://heycam.github.io/webidl/#dfn-effective-overload-set,
-           look for example right after the algorithm.
-
-           The output differs from spec because we don't implement the part
-           of the algorithm that handles variadic arguments."""
+           look for example right after the algorithm."""
         operation_list = [
             # f1: f(DOMString a)
             {'arguments': [{'idl_type_object': 'DOMString',
@@ -76,13 +78,14 @@
                            {'idl_type_object': 'double',
                             'is_optional': False,
                             'is_variadic': True}]}]
+
         overload_set = [
             # <f1, (DOMString), (required)>
             ({'arguments': [{'idl_type_object': 'DOMString',
                              'is_optional': False,
                              'is_variadic': False}]},
              ('DOMString',),
-             (False,)),
+             ('required',)),
             # <f2, (Node, DOMString, double), (required, required, variadic)>
             ({'arguments': [{'idl_type_object': 'Node',
                              'is_optional': False,
@@ -94,7 +97,20 @@
                              'is_optional': False,
                              'is_variadic': True}]},
              ('Node', 'DOMString', 'double'),
-             (False, False, True)),
+             ('required', 'required', 'variadic')),
+            # <f2, (Node, DOMString, double, double),
+            #      (required, required, variadic, variadic)>
+            ({'arguments': [{'idl_type_object': 'Node',
+                             'is_optional': False,
+                             'is_variadic': False},
+                            {'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': False},
+                            {'idl_type_object': 'double',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             ('Node', 'DOMString', 'double', 'double'),
+             ('required', 'required', 'variadic', 'variadic')),
             # <f2, (Node, DOMString), (required, required)>
             ({'arguments': [{'idl_type_object': 'Node',
                              'is_optional': False,
@@ -106,12 +122,11 @@
                              'is_optional': False,
                              'is_variadic': True}]},
              ('Node', 'DOMString'),
-             (False, False)),
-            # Missing from the output:
-            # <f2, (Node, DOMString, double, double),
-            #       (required, required, variadic, variadic)>,
+             ('required', 'required')),
             # <f3, (), ()>
-            ({'arguments': []}, (), ()),
+            ({'arguments': []},
+             (),
+             ()),
             # <f4, (Event, DOMString, DOMString, double),
             #      (required, required, optional, variadic)>
             ({'arguments': [{'idl_type_object': 'Event',
@@ -127,7 +142,7 @@
                              'is_optional': False,
                              'is_variadic': True}]},
              ('Event', 'DOMString', 'DOMString', 'double'),
-             (False, False, True, True)),
+             ('required', 'required', 'optional', 'variadic')),
             # <f4, (Event, DOMString, DOMString),
             #      (required, required, optional)>
             ({'arguments': [{'idl_type_object': 'Event',
@@ -143,7 +158,7 @@
                              'is_optional': False,
                              'is_variadic': True}]},
              ('Event', 'DOMString', 'DOMString'),
-             (False, False, True)),
+             ('required', 'required', 'optional')),
             # <f4, (Event, DOMString), (required, required)>
             ({'arguments': [{'idl_type_object': 'Event',
                              'is_optional': False,
@@ -158,6 +173,164 @@
                              'is_optional': False,
                              'is_variadic': True}]},
              ('Event', 'DOMString'),
-             (False, False))]
+             ('required', 'required'))]
+
+        self.assertEqual(effective_overload_set(operation_list), overload_set)
+
+    def test_element_create_proposed_syntax(self):
+        """Tests the proposed syntax for the convenience method Element.create.
+           Github issue: https://github.com/whatwg/dom/issues/477"""
+        operation_list = [
+            # f1: f(DOMString tag, Record<DOMString, DOMString> attrs, (Node or DOMString)... children)
+            {'arguments': [{'idl_type_object': 'DOMString',
+                            'is_optional': False,
+                            'is_variadic': False},
+                           {'idl_type_object': 'record<DOMString, DOMString>',
+                            'is_optional': False,
+                            'is_variadic': False},
+                           {'idl_type_object': 'NodeOrDOMString',
+                            'is_optional': False,
+                            'is_variadic': True}]},
+            # f2: f(DOMString tag, (Node or DOMString)... children)
+            {'arguments': [{'idl_type_object': 'DOMString',
+                            'is_optional': False,
+                            'is_variadic': False},
+                           {'idl_type_object': 'NodeOrDOMString',
+                            'is_optional': False,
+                            'is_variadic': True}]}]
+
+        overload_set = [
+            # <f1, (DOMString, Record, NodeOrDOMString), (required, required, variadic)>
+            ({'arguments': [{'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': False},
+                            {'idl_type_object': 'record<DOMString, DOMString>',
+                             'is_optional': False,
+                             'is_variadic': False},
+                            {'idl_type_object': 'NodeOrDOMString',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             ('DOMString', 'record<DOMString, DOMString>', 'NodeOrDOMString'),
+             ('required', 'required', 'variadic')),
+            # <f1, (DOMString, Record), (required, required)>
+            ({'arguments': [{'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': False},
+                            {'idl_type_object': 'record<DOMString, DOMString>',
+                             'is_optional': False,
+                             'is_variadic': False},
+                            {'idl_type_object': 'NodeOrDOMString',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             ('DOMString', 'record<DOMString, DOMString>'),
+             ('required', 'required')),
+            # <f2, (DOMString, NodeOrDOMString), (required, variadic)>
+            ({'arguments': [{'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': False},
+                            {'idl_type_object': 'NodeOrDOMString',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             ('DOMString', 'NodeOrDOMString'),
+             ('required', 'variadic')),
+            # <f2, (DOMString, NodeOrDOMString, NodeOrDOMString), (required, variadic, variadic)>
+            ({'arguments': [{'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': False},
+                            {'idl_type_object': 'NodeOrDOMString',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             ('DOMString', 'NodeOrDOMString', 'NodeOrDOMString'),
+             ('required', 'variadic', 'variadic')),
+            # <f2, (DOMString), (required)>
+            ({'arguments': [{'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': False},
+                            {'idl_type_object': 'NodeOrDOMString',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             ('DOMString',),
+             ('required',))]
+
+        self.assertEqual(effective_overload_set(operation_list), overload_set)
+
+    def test_optional_preceding_variadic(self):
+        operation_list = [
+            # f1: f(Node a, optional long b, DOMString... c)
+            {'arguments': [{'idl_type_object': 'Node',
+                            'is_optional': False,
+                            'is_variadic': False},
+                           {'idl_type_object': 'long',
+                            'is_optional': True,
+                            'is_variadic': False},
+                           {'idl_type_object': 'DOMString',
+                            'is_optional': False,
+                            'is_variadic': True}]},
+            # f2: f(DOMString... a)
+            {'arguments': [{'idl_type_object': 'DOMString',
+                            'is_optional': False,
+                            'is_variadic': True}]}]
+
+        overload_set = [
+            # <f1, (Node, long, DOMString), (required, optional, variadic)>
+            ({'arguments': [{'idl_type_object': 'Node',
+                             'is_optional': False,
+                             'is_variadic': False},
+                            {'idl_type_object': 'long',
+                             'is_optional': True,
+                             'is_variadic': False},
+                            {'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             ('Node', 'long', 'DOMString'),
+             ('required', 'optional', 'variadic')),
+            # <f1, (Node, long), (required, optional)>
+            ({'arguments': [{'idl_type_object': 'Node',
+                             'is_optional': False,
+                             'is_variadic': False},
+                            {'idl_type_object': 'long',
+                             'is_optional': True,
+                             'is_variadic': False},
+                            {'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             ('Node', 'long'),
+             ('required', 'optional')),
+            # <f1, (Node), (required)>
+            ({'arguments': [{'idl_type_object': 'Node',
+                             'is_optional': False,
+                             'is_variadic': False},
+                            {'idl_type_object': 'long',
+                             'is_optional': True,
+                             'is_variadic': False},
+                            {'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             ('Node',),
+             ('required',)),
+            # <f2, (DOMString), (variadic)>
+            ({'arguments': [{'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             ('DOMString',),
+             ('variadic',)),
+            # <f2, (DOMString, DOMString), (variadic, variadic)>
+            ({'arguments': [{'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             ('DOMString', 'DOMString'),
+             ('variadic', 'variadic')),
+            # <f2, (DOMString, DOMString, DOMString), (variadic, variadic, variadic)>
+            ({'arguments': [{'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             ('DOMString', 'DOMString', 'DOMString'),
+             ('variadic', 'variadic', 'variadic')),
+            # <f2, (), ()>
+            ({'arguments': [{'idl_type_object': 'DOMString',
+                             'is_optional': False,
+                             'is_variadic': True}]},
+             (),
+             ())]
 
         self.assertEqual(effective_overload_set(operation_list), overload_set)
diff --git a/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl b/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl
index e736908..8dc3c151 100644
--- a/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl
+++ b/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl
@@ -3,6 +3,8 @@
 {% include 'copyright_block.txt' %}
 #include "{{this_include_header_path}}"
 
+#include <algorithm>
+
 {% for filename in cpp_includes if filename != '%s.h' % cpp_class_or_partial %}
 #include "{{filename}}"
 {% endfor %}
diff --git a/third_party/blink/renderer/bindings/tests/idls/core/test_interface_node.idl b/third_party/blink/renderer/bindings/tests/idls/core/test_interface_node.idl
index a4ff40d3..4b5056dde 100644
--- a/third_party/blink/renderer/bindings/tests/idls/core/test_interface_node.idl
+++ b/third_party/blink/renderer/bindings/tests/idls/core/test_interface_node.idl
@@ -41,4 +41,7 @@
     TestInterfaceEmpty testInterfaceEmptyMethod();
     [PerWorldBindings] TestInterfaceEmpty perWorldBindingsTestInterfaceEmptyMethod();
     [PerWorldBindings] TestInterfaceEmpty perWorldBindingsTestInterfaceEmptyMethodOptionalBooleanArg(optional boolean optionalBooleanArgument);
+
+    TestInterfaceEmpty testInterfaceEmptyMethodOverloadWithVariadicArgs(boolean booleanArg, Node nodeArg, DOMString... domStringVariadicArgs);
+    TestInterfaceEmpty testInterfaceEmptyMethodOverloadWithVariadicArgs(boolean booleanArg, DOMString... domStringVariadicArgs);
 };
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer.cc
index 2eba168..fde7a1e8 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer_view.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer_view.cc
index 1555e248..d8a8e03 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer_view.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer_view.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer_view.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_data_view.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_data_view.cc
index 42b8494..561b32f7 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_data_view.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_data_view.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_data_view.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_svg_test_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_svg_test_interface.cc
index 5bf35fa..30f4adfa 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_svg_test_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_svg_test_interface.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_svg_test_interface.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_attributes.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_attributes.cc
index b84ba2ee..9e05a34 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_attributes.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_attributes.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_attributes.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_functions.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_functions.cc
index 35765b7..6b65b88 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_functions.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_functions.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_functions.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.cc
index b85ad5f..d4bed19 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.cc
index fc06abe..2416ce9 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.cc
index 6e58680..a9880d71 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
index a2ffb52f..18253e2 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/js_event_handler.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.cc
index 66f3f7f7..5b1b7ef 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.cc
index 8e2907a5..6ab7a5d 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/binding_security.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_conditional_secure_context.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_conditional_secure_context.cc
index 931f0b3..da1ddc7 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_conditional_secure_context.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_conditional_secure_context.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_conditional_secure_context.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc
index d5313903..0d4f73ff 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.cc
index 49616a0..ca2a49ce 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_3.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_3.cc
index fa1f218..3773147 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_3.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_3.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_3.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_4.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_4.cc
index 00442e0..f1dd214a 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_4.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_4.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_4.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_custom_constructor.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_custom_constructor.cc
index eb6c346..29d5671 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_custom_constructor.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_custom_constructor.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_custom_constructor.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.cc
index 91bd9317..e8ebf736 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_location.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_empty.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_empty.cc
index 9c1e665e..eb40bae 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_empty.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_empty.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_empty.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init_constructor.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init_constructor.cc
index 51eee84d..aeba2d64 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init_constructor.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init_constructor.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init_constructor.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.cc
index 28b9b16..b0d419c 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h"
 #include "third_party/blink/renderer/core/dom/document.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc
index 32a91d7..4b1839c 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.cc
index c3af467..c309903 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc
index 0131d22..e995014 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc
@@ -10,11 +10,14 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/js_event_handler.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_node.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_test_interface_empty.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/html/custom/v0_custom_element_processing_stack.h"
@@ -289,6 +292,95 @@
   V8SetReturnValueForMainWorld(info, impl->perWorldBindingsTestInterfaceEmptyMethodOptionalBooleanArg(optional_boolean_argument));
 }
 
+static void TestInterfaceEmptyMethodOverloadWithVariadicArgs1Method(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exception_state(info.GetIsolate(), ExceptionState::kExecutionContext, "TestInterfaceNode", "testInterfaceEmptyMethodOverloadWithVariadicArgs");
+
+  TestInterfaceNode* impl = V8TestInterfaceNode::ToImpl(info.Holder());
+
+  bool boolean_arg;
+  Node* node_arg;
+  Vector<String> dom_string_variadic_args;
+  boolean_arg = NativeValueTraits<IDLBoolean>::NativeValue(info.GetIsolate(), info[0], exception_state);
+  if (exception_state.HadException())
+    return;
+
+  node_arg = V8Node::ToImplWithTypeCheck(info.GetIsolate(), info[1]);
+  if (!node_arg) {
+    exception_state.ThrowTypeError("parameter 2 is not of type 'Node'.");
+    return;
+  }
+
+  dom_string_variadic_args = ToImplArguments<IDLString>(info, 2, exception_state);
+  if (exception_state.HadException())
+    return;
+
+  V8SetReturnValueFast(info, impl->testInterfaceEmptyMethodOverloadWithVariadicArgs(boolean_arg, node_arg, dom_string_variadic_args), impl);
+}
+
+static void TestInterfaceEmptyMethodOverloadWithVariadicArgs2Method(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exception_state(info.GetIsolate(), ExceptionState::kExecutionContext, "TestInterfaceNode", "testInterfaceEmptyMethodOverloadWithVariadicArgs");
+
+  TestInterfaceNode* impl = V8TestInterfaceNode::ToImpl(info.Holder());
+
+  bool boolean_arg;
+  Vector<String> dom_string_variadic_args;
+  boolean_arg = NativeValueTraits<IDLBoolean>::NativeValue(info.GetIsolate(), info[0], exception_state);
+  if (exception_state.HadException())
+    return;
+
+  dom_string_variadic_args = ToImplArguments<IDLString>(info, 1, exception_state);
+  if (exception_state.HadException())
+    return;
+
+  V8SetReturnValueFast(info, impl->testInterfaceEmptyMethodOverloadWithVariadicArgs(boolean_arg, dom_string_variadic_args), impl);
+}
+
+static void TestInterfaceEmptyMethodOverloadWithVariadicArgsMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  scheduler::CooperativeSchedulingManager::Instance()->Safepoint();
+
+  bool is_arity_error = false;
+
+  switch (std::min(3, info.Length())) {
+    case 1:
+      if (true) {
+        TestInterfaceEmptyMethodOverloadWithVariadicArgs2Method(info);
+        return;
+      }
+      break;
+    case 2:
+      if (V8Node::HasInstance(info[1], info.GetIsolate())) {
+        TestInterfaceEmptyMethodOverloadWithVariadicArgs1Method(info);
+        return;
+      }
+      if (true) {
+        TestInterfaceEmptyMethodOverloadWithVariadicArgs2Method(info);
+        return;
+      }
+      break;
+    case 3:
+      if (V8Node::HasInstance(info[1], info.GetIsolate())) {
+        TestInterfaceEmptyMethodOverloadWithVariadicArgs1Method(info);
+        return;
+      }
+      if (true) {
+        TestInterfaceEmptyMethodOverloadWithVariadicArgs2Method(info);
+        return;
+      }
+      break;
+    default:
+      is_arity_error = true;
+  }
+
+  ExceptionState exception_state(info.GetIsolate(), ExceptionState::kExecutionContext, "TestInterfaceNode", "testInterfaceEmptyMethodOverloadWithVariadicArgs");
+  if (is_arity_error) {
+    if (info.Length() < 1) {
+      exception_state.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length()));
+      return;
+    }
+  }
+  exception_state.ThrowTypeError("No function was found that matched the signature provided.");
+}
+
 }  // namespace test_interface_node_v8_internal
 
 void V8TestInterfaceNode::NodeNameAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -414,6 +506,12 @@
   test_interface_node_v8_internal::PerWorldBindingsTestInterfaceEmptyMethodOptionalBooleanArgMethodForMainWorld(info);
 }
 
+void V8TestInterfaceNode::TestInterfaceEmptyMethodOverloadWithVariadicArgsMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestInterfaceNode_testInterfaceEmptyMethodOverloadWithVariadicArgs");
+
+  test_interface_node_v8_internal::TestInterfaceEmptyMethodOverloadWithVariadicArgsMethod(info);
+}
+
 static constexpr V8DOMConfiguration::AccessorConfiguration kV8TestInterfaceNodeAccessors[] = {
     { "nodeName", V8TestInterfaceNode::NodeNameAttributeGetterCallback, V8TestInterfaceNode::NodeNameAttributeSetterCallback, V8PrivateProperty::kNoCachedAccessor, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAlwaysCallGetter, V8DOMConfiguration::kAllWorlds },
     { "stringAttribute", V8TestInterfaceNode::StringAttributeAttributeGetterCallback, V8TestInterfaceNode::StringAttributeAttributeSetterCallback, V8PrivateProperty::kNoCachedAccessor, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAlwaysCallGetter, V8DOMConfiguration::kAllWorlds },
@@ -431,6 +529,7 @@
     {"perWorldBindingsTestInterfaceEmptyMethod", V8TestInterfaceNode::PerWorldBindingsTestInterfaceEmptyMethodMethodCallback, 0, v8::None, V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kDoNotCheckAccess, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kNonMainWorlds},
     {"perWorldBindingsTestInterfaceEmptyMethodOptionalBooleanArg", V8TestInterfaceNode::PerWorldBindingsTestInterfaceEmptyMethodOptionalBooleanArgMethodCallbackForMainWorld, 0, v8::None, V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kDoNotCheckAccess, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kMainWorld},
     {"perWorldBindingsTestInterfaceEmptyMethodOptionalBooleanArg", V8TestInterfaceNode::PerWorldBindingsTestInterfaceEmptyMethodOptionalBooleanArgMethodCallback, 0, v8::None, V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kDoNotCheckAccess, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kNonMainWorlds},
+    {"testInterfaceEmptyMethodOverloadWithVariadicArgs", V8TestInterfaceNode::TestInterfaceEmptyMethodOverloadWithVariadicArgsMethodCallback, 1, v8::None, V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kDoNotCheckAccess, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAllWorlds},
 };
 
 static void InstallV8TestInterfaceNodeTemplate(
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.h b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.h
index 9cced93..f83f65f8 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.h
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.h
@@ -65,6 +65,7 @@
   CORE_EXPORT static void PerWorldBindingsTestInterfaceEmptyMethodMethodCallbackForMainWorld(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void PerWorldBindingsTestInterfaceEmptyMethodOptionalBooleanArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void PerWorldBindingsTestInterfaceEmptyMethodOptionalBooleanArgMethodCallbackForMainWorld(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void TestInterfaceEmptyMethodOverloadWithVariadicArgsMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
 
   static void InstallRuntimeEnabledFeaturesOnTemplate(
       v8::Isolate*,
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.cc
index cef580d..2e44452 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_secure_context.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_secure_context.cc
index 819d07a5..826c596 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_secure_context.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_secure_context.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_secure_context.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_node.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_node.cc
index 5c3968f..5bbf949 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_node.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_node.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_node.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
index 00e5e73..7e3d522 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_object.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/binding_security.h"
 #include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations.cc
index f7300ab..a2fabbc3 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations_not_enumerable.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations_not_enumerable.cc
index 01a351ac..4981915a 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations_not_enumerable.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations_not_enumerable.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations_not_enumerable.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.cc
index d294fb1..a1b07e15 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_variadic_constructor_arguments.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_variadic_constructor_arguments.cc
index ca56b9b7..6605d28 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_variadic_constructor_arguments.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_variadic_constructor_arguments.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_test_variadic_constructor_arguments.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_uint8_clamped_array.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_uint8_clamped_array.cc
index e5fc808a..3a8fdd57 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_uint8_clamped_array.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_uint8_clamped_array.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/core/v8_uint8_clamped_array.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_inherited_legacy_unenumerable_named_properties.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_inherited_legacy_unenumerable_named_properties.cc
index 93aef88c..87c851f7 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_inherited_legacy_unenumerable_named_properties.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_inherited_legacy_unenumerable_named_properties.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/modules/v8_test_inherited_legacy_unenumerable_named_properties.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_2_partial.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_2_partial.cc
index 77451f90..479d30e 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_2_partial.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_2_partial.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_2_partial.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.cc
index 902c2f8..9a9eb07b 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc
index 1ab0914..81937c6 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_not_enumerable_named_getter.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_not_enumerable_named_getter.cc
index b6688d8..4aef04c 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_not_enumerable_named_getter.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_not_enumerable_named_getter.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/modules/v8_test_not_enumerable_named_getter.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.cc
index 9c3d0a8..e474923 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.cc
@@ -10,6 +10,8 @@
 // clang-format off
 #include "third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.h"
 
+#include <algorithm>
+
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
diff --git a/third_party/blink/renderer/core/css/css_uri_value.cc b/third_party/blink/renderer/core/css/css_uri_value.cc
index ef22d47..ac64150 100644
--- a/third_party/blink/renderer/core/css/css_uri_value.cc
+++ b/third_party/blink/renderer/core/css/css_uri_value.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 
 namespace blink {
+namespace cssvalue {
 
 CSSURIValue::CSSURIValue(const AtomicString& relative_url,
                          const AtomicString& absolute_url)
@@ -73,4 +74,5 @@
   CSSValue::TraceAfterDispatch(visitor);
 }
 
+}  // namespace cssvalue
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_uri_value.h b/third_party/blink/renderer/core/css/css_uri_value.h
index fb0b746..3614f1b 100644
--- a/third_party/blink/renderer/core/css/css_uri_value.h
+++ b/third_party/blink/renderer/core/css/css_uri_value.h
@@ -15,6 +15,8 @@
 class KURL;
 class SVGResource;
 
+namespace cssvalue {
+
 class CSSURIValue : public CSSValue {
  public:
   static CSSURIValue* Create(const String& relative_url, const KURL& url) {
@@ -55,8 +57,10 @@
   mutable AtomicString absolute_url_;
 };
 
+}  // namespace cssvalue
+
 template <>
-struct DowncastTraits<CSSURIValue> {
+struct DowncastTraits<cssvalue::CSSURIValue> {
   static bool AllowFrom(const CSSValue& value) { return value.IsURIValue(); }
 };
 
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h
index ce5211a..e3c86b0 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h
@@ -22,10 +22,15 @@
 class CSSParserContext;
 class CSSPropertyValue;
 class CSSStringValue;
-class CSSURIValue;
 class CSSValuePair;
 class StylePropertyShorthand;
 
+namespace cssvalue {
+
+class CSSURIValue;
+
+}
+
 // When these functions are successful, they will consume all the relevant
 // tokens from the range and also consume any whitespace which follows. When
 // the start of the range doesn't match the type we're looking for, the range
@@ -89,7 +94,8 @@
                                         const CSSParserContext&);
 CSSStringValue* ConsumeString(CSSParserTokenRange&);
 StringView ConsumeUrlAsStringView(CSSParserTokenRange&);
-CSSURIValue* ConsumeUrl(CSSParserTokenRange&, const CSSParserContext*);
+cssvalue::CSSURIValue* ConsumeUrl(CSSParserTokenRange&,
+                                  const CSSParserContext*);
 
 CSSValue* ConsumeColor(CSSParserTokenRange&,
                        CSSParserMode,
diff --git a/third_party/blink/renderer/core/css/properties/longhands/clip_path_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/clip_path_custom.cc
index c12fa9f5..01bc430 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/clip_path_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/clip_path_custom.cc
@@ -20,7 +20,7 @@
                                            const CSSParserLocalContext&) const {
   if (range.Peek().Id() == CSSValueNone)
     return css_property_parser_helpers::ConsumeIdent(range);
-  if (CSSURIValue* url =
+  if (cssvalue::CSSURIValue* url =
           css_property_parser_helpers::ConsumeUrl(range, &context))
     return url;
   return css_parsing_utils::ConsumeBasicShape(range, context);
@@ -38,7 +38,7 @@
           style, ToShapeClipPathOperation(operation)->GetBasicShape());
     }
     if (operation->GetType() == ClipPathOperation::REFERENCE) {
-      return CSSURIValue::Create(
+      return cssvalue::CSSURIValue::Create(
           ToReferenceClipPathOperation(operation)->Url());
     }
   }
diff --git a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
index ed2fe98..e2a880b 100644
--- a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
+++ b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
@@ -51,6 +51,8 @@
 
 namespace blink {
 
+using namespace cssvalue;
+
 ElementStyleResources::ElementStyleResources(Element& element,
                                              float device_scale_factor)
     : element_(&element), device_scale_factor_(device_scale_factor) {}
diff --git a/third_party/blink/renderer/core/css/resolver/element_style_resources.h b/third_party/blink/renderer/core/css/resolver/element_style_resources.h
index 15d1c997..72e025d 100644
--- a/third_party/blink/renderer/core/css/resolver/element_style_resources.h
+++ b/third_party/blink/renderer/core/css/resolver/element_style_resources.h
@@ -38,7 +38,6 @@
 class CSSImageGeneratorValue;
 class CSSImageSetValue;
 class CSSImageValue;
-class CSSURIValue;
 class CSSValue;
 class ComputedStyle;
 class Element;
@@ -47,6 +46,12 @@
 class StylePendingImage;
 class TreeScope;
 
+namespace cssvalue {
+
+class CSSURIValue;
+
+}
+
 // Holds information about resources, requested by stylesheets.
 // Lifetime: per-element style resolve.
 class ElementStyleResources {
@@ -62,7 +67,7 @@
   enum AllowExternal { kDontAllowExternalResource, kAllowExternalResource };
   SVGResource* GetSVGResourceFromValue(
       TreeScope&,
-      const CSSURIValue&,
+      const cssvalue::CSSURIValue&,
       AllowExternal = kDontAllowExternalResource) const;
 
   void LoadPendingResources(ComputedStyle*);
diff --git a/third_party/blink/renderer/core/css/resolver/filter_operation_resolver.cc b/third_party/blink/renderer/core/css/resolver/filter_operation_resolver.cc
index f96da74..d16a560 100644
--- a/third_party/blink/renderer/core/css/resolver/filter_operation_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/filter_operation_resolver.cc
@@ -166,7 +166,8 @@
       state.CssToLengthConversionData();
 
   for (auto& curr_value : To<CSSValueList>(in_value)) {
-    if (const auto* url_value = DynamicTo<CSSURIValue>(curr_value.Get())) {
+    if (const auto* url_value =
+            DynamicTo<cssvalue::CSSURIValue>(curr_value.Get())) {
       CountFilterUse(FilterOperation::REFERENCE, state.GetDocument());
 
       SVGResource* resource =
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 011c50d..e17c071 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -2857,17 +2857,6 @@
   return nullptr;
 }
 
-HTMLSlotElement* Node::FinalDestinationSlot() const {
-  HTMLSlotElement* slot = AssignedSlot();
-  if (!slot)
-    return nullptr;
-  for (HTMLSlotElement* next = slot->AssignedSlot(); next;
-       next = next->AssignedSlot()) {
-    slot = next;
-  }
-  return slot;
-}
-
 HTMLSlotElement* Node::assignedSlotForBinding() {
   // assignedSlot doesn't need to recalc slot assignment
   if (ShadowRoot* root = V1ShadowRootOfParent()) {
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index 3665088..cf4bd2c 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -847,7 +847,6 @@
 
   StaticNodeList* getDestinationInsertionPoints();
   HTMLSlotElement* AssignedSlot() const;
-  HTMLSlotElement* FinalDestinationSlot() const;
   HTMLSlotElement* assignedSlotForBinding();
 
   bool IsFinishedParsingChildren() const {
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 1f80860..cd580127 100644
--- a/third_party/blink/renderer/core/editing/frame_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection_test.cc
@@ -142,7 +142,7 @@
     frame_rect.SetHeight(frame_rect.Height() + 1);
     GetDummyPageHolder().GetFrameView().SetFrameRect(frame_rect);
   }
-  std::unique_ptr<PaintController> paint_controller = PaintController::Create();
+  auto paint_controller = std::make_unique<PaintController>();
   {
     GraphicsContext context(*paint_controller);
     paint_controller->UpdateCurrentPaintChunkProperties(
diff --git a/third_party/blink/renderer/core/editing/selection_modifier.cc b/third_party/blink/renderer/core/editing/selection_modifier.cc
index 618144d..900d2b7 100644
--- a/third_party/blink/renderer/core/editing/selection_modifier.cc
+++ b/third_party/blink/renderer/core/editing/selection_modifier.cc
@@ -49,6 +49,10 @@
 
 namespace {
 
+// There are some cases where |SelectionModifier::ModifyWithPageGranularity()|
+// enters an infinite loop. Work around it by hard-limiting the iteration.
+const unsigned kMaxIterationForPageGranularityMovement = 1024;
+
 VisiblePosition LeftBoundaryOfLine(const VisiblePosition& c,
                                    TextDirection direction) {
   DCHECK(c.IsValid()) << c;
@@ -829,7 +833,11 @@
 
   VisiblePosition result;
   VisiblePosition next;
-  for (VisiblePosition p = pos;; p = next) {
+  unsigned iteration_count = 0;
+  for (VisiblePosition p = pos;
+       iteration_count < kMaxIterationForPageGranularityMovement; p = next) {
+    ++iteration_count;
+
     if (direction == SelectionModifyVerticalDirection::kUp)
       next = PreviousLinePosition(p, x_pos);
     else
diff --git a/third_party/blink/renderer/core/editing/selection_modifier_character.cc b/third_party/blink/renderer/core/editing/selection_modifier_character.cc
index a88c0db..07e1f28e 100644
--- a/third_party/blink/renderer/core/editing/selection_modifier_character.cc
+++ b/third_party/blink/renderer/core/editing/selection_modifier_character.cc
@@ -521,6 +521,145 @@
   }
 }
 
+// TODO(xiaochengh): This class will likely be reused elsewhere. Move it to its
+// own files.
+class InlineFormattingContextTraversal {
+  STATIC_ONLY(InlineFormattingContextTraversal);
+
+ public:
+  static LayoutBlockFlow* PreviousContextOf(const LayoutBlockFlow& start) {
+    const LayoutObject* stay_within = AncestorToStayWithin(start);
+    for (LayoutObject* runner =
+             start.PreviousInPostOrderBeforeChildren(stay_within);
+         runner;) {
+      if (ShouldNotEnter(*runner)) {
+        runner = runner->PreviousInPostOrderBeforeChildren(stay_within);
+        continue;
+      }
+
+      if (IsValidContextForCaretNavigation(*runner))
+        return ToLayoutBlockFlow(runner);
+
+      runner = runner->PreviousInPostOrder(stay_within);
+    }
+    return nullptr;
+  }
+
+  static LayoutBlockFlow* NextContextOf(const LayoutBlockFlow& start) {
+    const LayoutObject* stay_within = AncestorToStayWithin(start);
+    for (LayoutObject* runner = start.NextInPreOrderAfterChildren(stay_within);
+         runner;) {
+      if (ShouldNotEnter(*runner)) {
+        runner = runner->NextInPreOrderAfterChildren(stay_within);
+        continue;
+      }
+
+      if (IsValidContextForCaretNavigation(*runner))
+        return ToLayoutBlockFlow(runner);
+
+      runner = runner->NextInPreOrder(stay_within);
+    }
+    return nullptr;
+  }
+
+ private:
+  static bool IsValidContextForCaretNavigation(LayoutObject& object) {
+    if (!object.IsLayoutBlockFlow() || !object.ChildrenInline())
+      return false;
+
+    LayoutBlockFlow& block_flow = ToLayoutBlockFlow(object);
+    if (block_flow.IsLayoutNGMixin()) {
+      if (!block_flow.HasNGInlineNodeData() ||
+          !block_flow.GetNGInlineNodeData()->text_content.length()) {
+        return false;
+      }
+    }
+
+    const NGOffsetMapping* mapping =
+        NGInlineNode::GetOffsetMapping(&block_flow);
+    DCHECK(mapping);
+
+    // Reject empty blocks
+    // TODO(xiaochengh): We may need to support empty block.
+    if (!mapping->GetText().length())
+      return false;
+
+    // Reject if the block has CSS-generated contents only
+    for (const NGOffsetMappingUnit& unit : mapping->GetUnits()) {
+      if (unit.GetType() != NGOffsetMappingUnitType::kCollapsed &&
+          unit.AssociatedNode())
+        return true;
+    }
+    return false;
+  }
+
+  // TODO(xiaochengh): Move it to another place as it seems general.
+  static bool IsUserAgentShadowHost(const LayoutObject& object) {
+    if (!object.NonPseudoNode() || !object.NonPseudoNode()->IsElementNode())
+      return false;
+    const Element& element = *ToElement(object.NonPseudoNode());
+    return element.GetShadowRoot() && element.GetShadowRoot()->IsUserAgent();
+  }
+
+  static bool ShouldNotEnter(const LayoutObject& object) {
+    // Heuristic rules of whether caret movement can enter an object.
+    // TODO(xiaochengh): The rules will likely be extended in the future.
+    return IsUserAgentShadowHost(object);
+  }
+
+  static bool ShouldNotExit(const LayoutObject& object) {
+    // Heuristic rules of whether caret movement can exit an object.
+    // TODO(xiaochengh): The rules will likely be extended in the future.
+    return IsUserAgentShadowHost(object);
+  }
+
+  static const LayoutObject* AncestorToStayWithin(
+      const LayoutBlockFlow& start) {
+    for (const LayoutObject* runner = start.Parent(); runner;
+         runner = runner->Parent()) {
+      if (ShouldNotExit(*runner))
+        return runner;
+    }
+    return nullptr;
+  }
+};
+
+template <typename Strategy, typename Traversal>
+PositionWithAffinityTemplate<Strategy> TraverseOutOfCurrentContext(
+    const LayoutBlockFlow& context,
+    NGCaretNavigator::VisualMovementResultType type) {
+  // TODO(xiaochengh): Handle the case where we move out from an inline block
+  // into its parent/ancestor inline formatting context.
+
+  const bool is_before_context =
+      type == NGCaretNavigator::VisualMovementResultType::kBeforeContext;
+  LayoutBlockFlow* target_context =
+      is_before_context
+          ? InlineFormattingContextTraversal::PreviousContextOf(context)
+          : InlineFormattingContextTraversal::NextContextOf(context);
+  if (!target_context)
+    return PositionWithAffinityTemplate<Strategy>();
+
+  if (!target_context->IsLayoutNGMixin()) {
+    // We have traversed to a legacy block.
+    // TODO(xiaochengh): In most cases, we reach here by crossing an editability
+    // boundary, for which we can simply return null. Investigate if there are
+    // other cases that require a non-trivial fallback.
+    return PositionWithAffinityTemplate<Strategy>();
+  }
+
+  NGCaretNavigator target_navigator(*target_context);
+  const NGCaretNavigator::Position result =
+      is_before_context
+          ? Traversal::MostBackwardPositionInLastLine(target_navigator)
+          : Traversal::MostBackwardPositionInFirstLine(target_navigator);
+
+  DCHECK(NGInlineNode::GetOffsetMapping(target_context));
+  return FromPositionInDOMTree<Strategy>(
+      NGInlineNode::GetOffsetMapping(target_context)
+          ->GetPositionWithAffinity(result));
+}
+
 template <typename Strategy, typename Traversal>
 PositionWithAffinityTemplate<Strategy> TraverseIntoChildContext(
     const PositionTemplate<Strategy>& position) {
@@ -640,14 +779,10 @@
   }
 
   // We reach here if we need to move out of the current block.
-  if (result_caret_position.IsBeforeContext()) {
-    // TODO(xiaochengh): Move to the visual end of the previous block.
-    return PositionWithAffinityTemplate<Strategy>();
-  }
-
-  DCHECK(result_caret_position.IsAfterContext());
-  // TODO(xiaochengh): Move to the visual beginning of the next block.
-  return PositionWithAffinityTemplate<Strategy>();
+  DCHECK(result_caret_position.IsBeforeContext() ||
+         result_caret_position.IsAfterContext());
+  return TraverseOutOfCurrentContext<Strategy, Traversal>(
+      *context, result_caret_position.type);
 }
 
 template <typename Strategy, typename Traversal>
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
index 336fff83..37363f43 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
@@ -1513,7 +1513,7 @@
   const auto* plugin =
       static_cast<const CompositedPlugin*>(container->Plugin());
 
-  std::unique_ptr<PaintController> paint_controller = PaintController::Create();
+  auto paint_controller = std::make_unique<PaintController>();
   paint_controller->UpdateCurrentPaintChunkProperties(
       base::nullopt, PropertyTreeState::Root());
   GraphicsContext graphics_context(*paint_controller);
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 b27e24a..9d06e343 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1258,6 +1258,12 @@
   // deleted.
   AsView().client = nullptr;
 
+  // TODO(crbug.com/939262): After this the WebWidget for main frame local root
+  // is closed, and the WebWidgetClient should not be used. There appears to be
+  // pointers left over still pointing to the WebWidgetClient and being used
+  // though.
+  static_cast<ChromeClientImpl*>(chrome_client_.Get())->SetClosed();
+
   Release();  // Balances a reference acquired in WebView::Create
 }
 
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
index e0d298e..72452876 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
@@ -878,8 +878,6 @@
   switch (directive_type) {
     case ContentSecurityPolicy::DirectiveType::kScriptSrcAttr:
     case ContentSecurityPolicy::DirectiveType::kStyleSrcAttr:
-      if (!policy_->ExperimentalFeaturesEnabled())
-        return false;
       if (!CheckUnsafeHashesAllowed(OperativeDirective(directive_type)))
         return false;
       break;
@@ -1279,12 +1277,10 @@
   } else if (type == ContentSecurityPolicy::DirectiveType::kScriptSrc) {
     SetCSPDirective<SourceListDirective>(name, value, script_src_);
     policy_->UsesScriptHashAlgorithms(script_src_->HashAlgorithmsUsed());
-  } else if (type == ContentSecurityPolicy::DirectiveType::kScriptSrcAttr &&
-             policy_->ExperimentalFeaturesEnabled()) {
+  } else if (type == ContentSecurityPolicy::DirectiveType::kScriptSrcAttr) {
     SetCSPDirective<SourceListDirective>(name, value, script_src_attr_);
     policy_->UsesScriptHashAlgorithms(script_src_attr_->HashAlgorithmsUsed());
-  } else if (type == ContentSecurityPolicy::DirectiveType::kScriptSrcElem &&
-             policy_->ExperimentalFeaturesEnabled()) {
+  } else if (type == ContentSecurityPolicy::DirectiveType::kScriptSrcElem) {
     SetCSPDirective<SourceListDirective>(name, value, script_src_elem_);
     policy_->UsesScriptHashAlgorithms(script_src_elem_->HashAlgorithmsUsed());
   } else if (type == ContentSecurityPolicy::DirectiveType::kObjectSrc) {
@@ -1298,12 +1294,10 @@
   } else if (type == ContentSecurityPolicy::DirectiveType::kStyleSrc) {
     SetCSPDirective<SourceListDirective>(name, value, style_src_);
     policy_->UsesStyleHashAlgorithms(style_src_->HashAlgorithmsUsed());
-  } else if (type == ContentSecurityPolicy::DirectiveType::kStyleSrcAttr &&
-             policy_->ExperimentalFeaturesEnabled()) {
+  } else if (type == ContentSecurityPolicy::DirectiveType::kStyleSrcAttr) {
     SetCSPDirective<SourceListDirective>(name, value, style_src_attr_);
     policy_->UsesStyleHashAlgorithms(style_src_attr_->HashAlgorithmsUsed());
-  } else if (type == ContentSecurityPolicy::DirectiveType::kStyleSrcElem &&
-             policy_->ExperimentalFeaturesEnabled()) {
+  } else if (type == ContentSecurityPolicy::DirectiveType::kStyleSrcElem) {
     SetCSPDirective<SourceListDirective>(name, value, style_src_elem_);
     policy_->UsesStyleHashAlgorithms(style_src_elem_->HashAlgorithmsUsed());
   } else if (type == ContentSecurityPolicy::DirectiveType::kFontSrc) {
@@ -1464,23 +1458,19 @@
       directive = script_src_;
       break;
     case ContentSecurityPolicy::DirectiveType::kScriptSrcAttr:
-      directive = policy_->ExperimentalFeaturesEnabled() ? script_src_attr_
-                                                         : script_src_;
+      directive = script_src_attr_;
       break;
     case ContentSecurityPolicy::DirectiveType::kScriptSrcElem:
-      directive = policy_->ExperimentalFeaturesEnabled() ? script_src_elem_
-                                                         : script_src_;
+      directive = script_src_elem_;
       break;
     case ContentSecurityPolicy::DirectiveType::kStyleSrc:
       directive = style_src_;
       break;
     case ContentSecurityPolicy::DirectiveType::kStyleSrcAttr:
-      directive =
-          policy_->ExperimentalFeaturesEnabled() ? style_src_attr_ : style_src_;
+      directive = style_src_attr_;
       break;
     case ContentSecurityPolicy::DirectiveType::kStyleSrcElem:
-      directive =
-          policy_->ExperimentalFeaturesEnabled() ? style_src_elem_ : style_src_;
+      directive = style_src_elem_;
       break;
     case ContentSecurityPolicy::DirectiveType::kFrameSrc:
       directive = frame_src_;
diff --git a/third_party/blink/renderer/core/frame/frame_overlay.cc b/third_party/blink/renderer/core/frame/frame_overlay.cc
index 191eba6..75c609b 100644
--- a/third_party/blink/renderer/core/frame/frame_overlay.cc
+++ b/third_party/blink/renderer/core/frame/frame_overlay.cc
@@ -80,7 +80,7 @@
   if (!layer_) {
     if (!parent_layer)
       return;
-    layer_ = GraphicsLayer::Create(*this);
+    layer_ = std::make_unique<GraphicsLayer>(*this);
     layer_->SetDrawsContent(true);
 
     if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
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 7b875bf..134edd7 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2640,7 +2640,7 @@
 
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
     if (!paint_controller_)
-      paint_controller_ = PaintController::Create();
+      paint_controller_ = std::make_unique<PaintController>();
 
     // TODO(crbug.com/917911): Painting of overlays should not force repainting
     // of the frame contents.
@@ -2765,7 +2765,7 @@
 
   if (!paint_artifact_compositor_) {
     paint_artifact_compositor_ =
-        PaintArtifactCompositor::Create(WTF::BindRepeating(
+        std::make_unique<PaintArtifactCompositor>(WTF::BindRepeating(
             &ScrollingCoordinator::DidScroll,
             // The layer being scrolled is destroyed before the
             // ScrollingCoordinator.
@@ -2800,7 +2800,8 @@
     // collect the foreign layers which doesn't need caching. It also
     // shouldn't affect caching status of DisplayItemClients because it's
     // FinishCycle() is not synchronized with other PaintControllers.
-    paint_controller_ = PaintController::Create(PaintController::kTransient);
+    paint_controller_ =
+        std::make_unique<PaintController>(PaintController::kTransient);
 
     GraphicsContext context(*paint_controller_);
     // Note: Some blink unit tests run without turning on compositing which
diff --git a/third_party/blink/renderer/core/frame/visual_viewport.cc b/third_party/blink/renderer/core/frame/visual_viewport.cc
index acbb8f2d..7958bf4 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport.cc
@@ -564,17 +564,17 @@
   needs_paint_property_update_ = true;
 
   // FIXME: The root transform layer should only be created on demand.
-  root_transform_layer_ = GraphicsLayer::Create(*this);
-  inner_viewport_container_layer_ = GraphicsLayer::Create(*this);
+  root_transform_layer_ = std::make_unique<GraphicsLayer>(*this);
+  inner_viewport_container_layer_ = std::make_unique<GraphicsLayer>(*this);
   // TODO(crbug.com/836884) Should remove overscroll_elasticity_layer_ after
   // BGPT landed.
   if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
-    overscroll_elasticity_layer_ = GraphicsLayer::Create(*this);
+    overscroll_elasticity_layer_ = std::make_unique<GraphicsLayer>(*this);
     overscroll_elasticity_layer_->SetElementId(
         GetCompositorOverscrollElasticityElementId());
   }
-  page_scale_layer_ = GraphicsLayer::Create(*this);
-  inner_viewport_scroll_layer_ = GraphicsLayer::Create(*this);
+  page_scale_layer_ = std::make_unique<GraphicsLayer>(*this);
+  inner_viewport_scroll_layer_ = std::make_unique<GraphicsLayer>(*this);
 
   ScrollingCoordinator* coordinator = GetPage().GetScrollingCoordinator();
   DCHECK(coordinator);
@@ -643,8 +643,8 @@
       !GetPage().GetSettings().GetHideScrollbars()) {
     DCHECK(!overlay_scrollbar_horizontal_);
     DCHECK(!overlay_scrollbar_vertical_);
-    overlay_scrollbar_horizontal_ = GraphicsLayer::Create(*this);
-    overlay_scrollbar_vertical_ = GraphicsLayer::Create(*this);
+    overlay_scrollbar_horizontal_ = std::make_unique<GraphicsLayer>(*this);
+    overlay_scrollbar_vertical_ = std::make_unique<GraphicsLayer>(*this);
     SetupScrollbar(kHorizontalScrollbar);
     SetupScrollbar(kVerticalScrollbar);
   } else {
diff --git a/third_party/blink/renderer/core/html/custom/custom_element.cc b/third_party/blink/renderer/core/html/custom/custom_element.cc
index 8e4c863..3dd0cfa 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element.cc
+++ b/third_party/blink/renderer/core/html/custom/custom_element.cc
@@ -283,12 +283,13 @@
   }
 }
 
-void CustomElement::EnqueueRestoreValueCallback(Element& element,
-                                                const FileOrUSVString& value,
-                                                const String& mode) {
+void CustomElement::EnqueueRestoreStateCallback(
+    Element& element,
+    const FileOrUSVStringOrFormData& value,
+    const String& mode) {
   auto& definition = *DefinitionForElementWithoutCheck(element);
-  if (definition.HasRestoreValueCallback()) {
-    Enqueue(element, CustomElementReactionFactory::CreateRestoreValue(
+  if (definition.HasRestoreStateCallback()) {
+    Enqueue(element, CustomElementReactionFactory::CreateRestoreState(
                          definition, value, mode));
   }
 }
diff --git a/third_party/blink/renderer/core/html/custom/custom_element.h b/third_party/blink/renderer/core/html/custom/custom_element.h
index e5bfe62..3f26bfe8de 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element.h
+++ b/third_party/blink/renderer/core/html/custom/custom_element.h
@@ -17,7 +17,7 @@
 
 class Document;
 class Element;
-class FileOrUSVString;
+class FileOrUSVStringOrFormData;
 class HTMLElement;
 class HTMLFormElement;
 class QualifiedName;
@@ -110,9 +110,10 @@
   static void EnqueueFormResetCallback(Element& element);
   static void EnqueueDisabledStateChangedCallback(Element& element,
                                                   bool is_disabled);
-  static void EnqueueRestoreValueCallback(Element& element,
-                                          const FileOrUSVString& value,
-                                          const String& mode);
+  static void EnqueueRestoreStateCallback(
+      Element& element,
+      const FileOrUSVStringOrFormData& value,
+      const String& mode);
 
   static void TryToUpgrade(Element&, bool upgrade_invisible_elements = false);
 
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_definition.h b/third_party/blink/renderer/core/html/custom/custom_element_definition.h
index eeaea76..da498f7 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element_definition.h
+++ b/third_party/blink/renderer/core/html/custom/custom_element_definition.h
@@ -22,7 +22,7 @@
 class Document;
 class Element;
 class ExceptionState;
-class FileOrUSVString;
+class FileOrUSVStringOrFormData;
 class HTMLElement;
 class HTMLFormElement;
 class QualifiedName;
@@ -78,7 +78,7 @@
   virtual bool HasFormAssociatedCallback() const = 0;
   virtual bool HasFormResetCallback() const = 0;
   virtual bool HasDisabledStateChangedCallback() const = 0;
-  virtual bool HasRestoreValueCallback() const = 0;
+  virtual bool HasRestoreStateCallback() const = 0;
 
   virtual void RunConnectedCallback(Element&) = 0;
   virtual void RunDisconnectedCallback(Element&) = 0;
@@ -94,8 +94,8 @@
   virtual void RunFormResetCallback(Element& element) = 0;
   virtual void RunDisabledStateChangedCallback(Element& element,
                                                bool is_disabled) = 0;
-  virtual void RunRestoreValueCallback(Element& element,
-                                       const FileOrUSVString& value,
+  virtual void RunRestoreStateCallback(Element& element,
+                                       const FileOrUSVStringOrFormData& value,
                                        const String& mode) = 0;
 
   void EnqueueUpgradeReaction(Element&,
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_reaction_factory.cc b/third_party/blink/renderer/core/html/custom/custom_element_reaction_factory.cc
index 1145659..2798f41 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element_reaction_factory.cc
+++ b/third_party/blink/renderer/core/html/custom/custom_element_reaction_factory.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/html/custom/custom_element_reaction_factory.h"
 
-#include "third_party/blink/renderer/bindings/core/v8/file_or_usv_string.h"
+#include "third_party/blink/renderer/bindings/core/v8/file_or_usv_string_or_form_data.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/html/custom/custom_element_definition.h"
 #include "third_party/blink/renderer/core/html/custom/custom_element_reaction.h"
@@ -205,14 +205,15 @@
 
 // ----------------------------------------------------------------
 
-class CustomElementRestoreValueCallbackReaction final
+class CustomElementRestoreStateCallbackReaction final
     : public CustomElementReaction {
  public:
-  CustomElementRestoreValueCallbackReaction(CustomElementDefinition& definition,
-                                            const FileOrUSVString& value,
-                                            const String& mode)
+  CustomElementRestoreStateCallbackReaction(
+      CustomElementDefinition& definition,
+      const FileOrUSVStringOrFormData& value,
+      const String& mode)
       : CustomElementReaction(definition), value_(value), mode_(mode) {
-    DCHECK(definition.HasRestoreValueCallback());
+    DCHECK(definition.HasRestoreStateCallback());
     DCHECK(mode == "restore" || mode == "autocomplete");
   }
 
@@ -223,13 +224,13 @@
 
  private:
   void Invoke(Element& element) override {
-    definition_->RunRestoreValueCallback(element, value_, mode_);
+    definition_->RunRestoreStateCallback(element, value_, mode_);
   }
 
-  FileOrUSVString value_;
+  FileOrUSVStringOrFormData value_;
   String mode_;
 
-  DISALLOW_COPY_AND_ASSIGN(CustomElementRestoreValueCallbackReaction);
+  DISALLOW_COPY_AND_ASSIGN(CustomElementRestoreStateCallbackReaction);
 };
 
 // ----------------------------------------------------------------
@@ -291,11 +292,11 @@
                                                          is_disabled);
 }
 
-CustomElementReaction& CustomElementReactionFactory::CreateRestoreValue(
+CustomElementReaction& CustomElementReactionFactory::CreateRestoreState(
     CustomElementDefinition& definition,
-    const FileOrUSVString& value,
+    const FileOrUSVStringOrFormData& value,
     const String& mode) {
-  return *MakeGarbageCollected<CustomElementRestoreValueCallbackReaction>(
+  return *MakeGarbageCollected<CustomElementRestoreStateCallbackReaction>(
       definition, value, mode);
 }
 
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_reaction_factory.h b/third_party/blink/renderer/core/html/custom/custom_element_reaction_factory.h
index 8728bc9..9b27868 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element_reaction_factory.h
+++ b/third_party/blink/renderer/core/html/custom/custom_element_reaction_factory.h
@@ -13,7 +13,7 @@
 class CustomElementDefinition;
 class CustomElementReaction;
 class Document;
-class FileOrUSVString;
+class FileOrUSVStringOrFormData;
 class HTMLFormElement;
 class QualifiedName;
 
@@ -45,9 +45,9 @@
   static CustomElementReaction& CreateDisabledStateChanged(
       CustomElementDefinition& definition,
       bool is_disabled);
-  static CustomElementReaction& CreateRestoreValue(
+  static CustomElementReaction& CreateRestoreState(
       CustomElementDefinition& definition,
-      const FileOrUSVString& value,
+      const FileOrUSVStringOrFormData& value,
       const String& mode);
 };
 
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_registry.idl b/third_party/blink/renderer/core/html/custom/custom_element_registry.idl
index 08e89a8..0144b8f 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element_registry.idl
+++ b/third_party/blink/renderer/core/html/custom/custom_element_registry.idl
@@ -18,5 +18,5 @@
 callback CustomElementAttributeChangedCallback = void (DOMString localName, DOMString? oldValue, DOMString? newValue, USVString? attrNamespace);
 callback CustomElementFormAssociatedCallback = void (HTMLFormElement? form);
 callback CustomElementDisabledStateChangedCallback = void (boolean disabled);
-enum RestoreValueMode { "restore", "autocomplete" };
-callback CustomElementRestoreValueCallback = void (FormDataEntryValue value, RestoreValueMode mode);
+enum RestoreStateMode { "restore", "autocomplete" };
+callback CustomElementRestoreStateCallback = void (ControlValue value, RestoreStateMode mode);
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_test_helpers.h b/third_party/blink/renderer/core/html/custom/custom_element_test_helpers.h
index 97d9c51..b144899 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element_test_helpers.h
+++ b/third_party/blink/renderer/core/html/custom/custom_element_test_helpers.h
@@ -80,7 +80,7 @@
   bool HasFormAssociatedCallback() const override { return false; }
   bool HasFormResetCallback() const override { return false; }
   bool HasDisabledStateChangedCallback() const override { return false; }
-  bool HasRestoreValueCallback() const override { return false; }
+  bool HasRestoreStateCallback() const override { return false; }
 
   void RunConnectedCallback(Element&) override {
     NOTREACHED() << "definition does not have connected callback";
@@ -116,8 +116,8 @@
     NOTREACHED() << "definition does not have disabledStateChangedCallback";
   }
 
-  void RunRestoreValueCallback(Element& element,
-                               const FileOrUSVString& value,
+  void RunRestoreStateCallback(Element& element,
+                               const FileOrUSVStringOrFormData& value,
                                const String& mode) override {
     NOTREACHED() << "definition does not have restoreValueCallback";
   }
diff --git a/third_party/blink/renderer/core/html/custom/element_internals.cc b/third_party/blink/renderer/core/html/custom/element_internals.cc
index c9bd305..a62ca80 100644
--- a/third_party/blink/renderer/core/html/custom/element_internals.cc
+++ b/third_party/blink/renderer/core/html/custom/element_internals.cc
@@ -37,19 +37,19 @@
 void ElementInternals::Trace(Visitor* visitor) {
   visitor->Trace(target_);
   visitor->Trace(value_);
-  visitor->Trace(entry_source_);
+  visitor->Trace(state_);
   visitor->Trace(validity_flags_);
   ListedElement::Trace(visitor);
   ScriptWrappable::Trace(visitor);
 }
 
-void ElementInternals::setFormValue(const FileOrUSVString& value,
+void ElementInternals::setFormValue(const ControlValue& value,
                                     ExceptionState& exception_state) {
-  setFormValue(value, nullptr, exception_state);
+  setFormValue(value, value, exception_state);
 }
 
-void ElementInternals::setFormValue(const FileOrUSVString& value,
-                                    FormData* entry_source,
+void ElementInternals::setFormValue(const ControlValue& value,
+                                    const ControlValue& state,
                                     ExceptionState& exception_state) {
   if (!IsTargetFormAssociated()) {
     exception_state.ThrowDOMException(
@@ -57,13 +57,22 @@
         "The target element is not a form-associated custom element.");
     return;
   }
-  if (!entry_source) {
+
+  if (value.IsFormData()) {
+    value_ = ControlValue::FromFormData(
+        MakeGarbageCollected<FormData>(*value.GetAsFormData()));
+  } else {
     value_ = value;
-    entry_source_ = nullptr;
-    return;
   }
-  value_ = value;
-  entry_source_ = MakeGarbageCollected<FormData>(*entry_source);
+
+  if (&value == &state) {
+    state_ = value_;
+  } else if (state.IsFormData()) {
+    state_ = ControlValue::FromFormData(
+        MakeGarbageCollected<FormData>(*state.GetAsFormData()));
+  } else {
+    state_ = state;
+  }
   NotifyFormStateChanged();
 }
 
@@ -230,7 +239,7 @@
   if (Target().IsDisabledFormControl())
     return;
   const AtomicString& name = Target().FastGetAttribute(html_names::kNameAttr);
-  if (!entry_source_) {
+  if (!value_.IsFormData()) {
     if (name.IsEmpty())
       return;
     if (value_.IsFile())
@@ -240,7 +249,7 @@
     // Append nothing for null value.
     return;
   }
-  for (const auto& entry : entry_source_->Entries()) {
+  for (const auto& entry : value_.GetAsFormData()->Entries()) {
     if (entry->isFile())
       form_data.append(entry->name(), entry->GetFile());
     else
@@ -319,6 +328,9 @@
     state.Append("File");
     File* file = value_.GetAsFile();
     file->AppendToControlState(state);
+  } else if (value_.IsFormData()) {
+    state.Append("FormData");
+    value_.GetAsFormData()->AppendToControlState(state);
   }
   // Add nothing if value_.IsNull().
   return state;
@@ -328,14 +340,18 @@
   if (state.ValueSize() < 2)
     return;
   if (state[0] == "USVString") {
-    value_ = FileOrUSVString::FromUSVString(state[1]);
+    value_ = ControlValue::FromUSVString(state[1]);
   } else if (state[0] == "File") {
     wtf_size_t i = 1;
     if (auto* file = File::CreateFromControlState(state, i))
-      value_ = FileOrUSVString::FromFile(file);
+      value_ = ControlValue::FromFile(file);
+  } else if (state[0] == "FormData") {
+    wtf_size_t i = 1;
+    if (auto* form_data = FormData::CreateFromControlState(state, i))
+      value_ = ControlValue::FromFormData(form_data);
   }
   if (!value_.IsNull())
-    CustomElement::EnqueueRestoreValueCallback(Target(), value_, "restore");
+    CustomElement::EnqueueRestoreStateCallback(Target(), value_, "restore");
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/custom/element_internals.h b/third_party/blink/renderer/core/html/custom/element_internals.h
index fb3909c..fff80f92 100644
--- a/third_party/blink/renderer/core/html/custom/element_internals.h
+++ b/third_party/blink/renderer/core/html/custom/element_internals.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CUSTOM_ELEMENT_INTERNALS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CUSTOM_ELEMENT_INTERNALS_H_
 
-#include "third_party/blink/renderer/bindings/core/v8/file_or_usv_string.h"
+#include "third_party/blink/renderer/bindings/core/v8/file_or_usv_string_or_form_data.h"
 #include "third_party/blink/renderer/core/html/forms/listed_element.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
@@ -27,11 +27,11 @@
   HTMLElement& Target() const { return *target_; }
   void DidUpgrade();
 
+  using ControlValue = FileOrUSVStringOrFormData;
   // IDL attributes/operations
-  void setFormValue(const FileOrUSVString& value,
-                    ExceptionState& exception_state);
-  void setFormValue(const FileOrUSVString& value,
-                    FormData* entry_source,
+  void setFormValue(const ControlValue& value, ExceptionState& exception_state);
+  void setFormValue(const ControlValue& value,
+                    const ControlValue& state,
                     ExceptionState& exception_state);
   HTMLFormElement* form(ExceptionState& exception_state) const;
   void setValidity(ValidityStateFlags* flags, ExceptionState& exception_state);
@@ -74,8 +74,8 @@
 
   Member<HTMLElement> target_;
 
-  FileOrUSVString value_;
-  Member<FormData> entry_source_;
+  ControlValue value_;
+  ControlValue state_;
   bool is_disabled_ = false;
   Member<ValidityStateFlags> validity_flags_;
 
diff --git a/third_party/blink/renderer/core/html/custom/element_internals.idl b/third_party/blink/renderer/core/html/custom/element_internals.idl
index c324e8d..1786ffd 100644
--- a/third_party/blink/renderer/core/html/custom/element_internals.idl
+++ b/third_party/blink/renderer/core/html/custom/element_internals.idl
@@ -4,13 +4,15 @@
 
 // https://docs.google.com/document/d/1JO8puctCSpW-ZYGU8lF-h4FWRIDQNDVexzHoOQ2iQmY/edit?pli=1#heading=h.pjt9nhs3gu3k
 
+typedef (File or USVString or FormData) ControlValue;
+
 [
   Exposed=Window,
   RuntimeEnabled=ElementInternals
 ]
 interface ElementInternals {
   // Attributes and operations for form-associated custom elements.
-  [RaisesException] void setFormValue(FormDataEntryValue? value, optional FormData entrySource);
+  [RaisesException] void setFormValue(ControlValue? value, optional ControlValue? state);
 
   [RaisesException] readonly attribute HTMLFormElement? form;
 
diff --git a/third_party/blink/renderer/core/html/forms/form_controller.h b/third_party/blink/renderer/core/html/forms/form_controller.h
index a560ed1fe..6514741 100644
--- a/third_party/blink/renderer/core/html/forms/form_controller.h
+++ b/third_party/blink/renderer/core/html/forms/form_controller.h
@@ -47,8 +47,9 @@
   explicit FormControlState(const String& value) : type_(kTypeRestore) {
     values_.push_back(value);
   }
-  static FormControlState Deserialize(const Vector<String>& state_vector,
-                                      wtf_size_t& index);
+  CORE_EXPORT static FormControlState Deserialize(
+      const Vector<String>& state_vector,
+      wtf_size_t& index);
   FormControlState(const FormControlState& another) = default;
   FormControlState& operator=(const FormControlState&);
 
diff --git a/third_party/blink/renderer/core/html/forms/form_data.cc b/third_party/blink/renderer/core/html/forms/form_data.cc
index 39ee396..1fad6ad 100644
--- a/third_party/blink/renderer/core/html/forms/form_data.cc
+++ b/third_party/blink/renderer/core/html/forms/form_data.cc
@@ -34,6 +34,7 @@
 #include "third_party/blink/renderer/core/fileapi/blob.h"
 #include "third_party/blink/renderer/core/fileapi/file.h"
 #include "third_party/blink/renderer/core/frame/use_counter.h"
+#include "third_party/blink/renderer/core/html/forms/form_controller.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/network/form_data_encoder.h"
@@ -367,4 +368,46 @@
                       GetBlob()->GetBlobDataHandle());
 }
 
+void FormData::AppendToControlState(FormControlState& state) const {
+  state.Append(String::Number(size()));
+  for (const auto& entry : Entries()) {
+    state.Append(entry->name());
+    if (entry->isFile()) {
+      state.Append("File");
+      entry->GetFile()->AppendToControlState(state);
+    } else {
+      state.Append("USVString");
+      state.Append(entry->Value());
+    }
+  }
+}
+
+FormData* FormData::CreateFromControlState(const FormControlState& state,
+                                           wtf_size_t& index) {
+  bool ok = false;
+  uint64_t length = state[index].ToUInt64Strict(&ok);
+  if (!ok)
+    return nullptr;
+  auto* form_data = MakeGarbageCollected<FormData>();
+  ++index;
+  for (uint64_t j = 0; j < length; ++j) {
+    // Need at least three items.
+    if (index + 2 >= state.ValueSize())
+      return nullptr;
+    const String& name = state[index++];
+    const String& entry_type = state[index++];
+    if (entry_type == "File") {
+      if (auto* file = File::CreateFromControlState(state, index))
+        form_data->append(name, file);
+      else
+        return nullptr;
+    } else if (entry_type == "USVString") {
+      form_data->append(name, state[index++]);
+    } else {
+      return nullptr;
+    }
+  }
+  return form_data;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/form_data.h b/third_party/blink/renderer/core/html/forms/form_data.h
index 8335bbe..75b0cb6 100644
--- a/third_party/blink/renderer/core/html/forms/form_data.h
+++ b/third_party/blink/renderer/core/html/forms/form_data.h
@@ -42,6 +42,7 @@
 namespace blink {
 
 class Blob;
+class FormControlState;
 class HTMLFormElement;
 class ScriptState;
 
@@ -106,6 +107,10 @@
       EncodedFormData::EncodingType = EncodedFormData::kFormURLEncoded);
   scoped_refptr<EncodedFormData> EncodeMultiPartFormData();
 
+  void AppendToControlState(FormControlState& state) const;
+  static FormData* CreateFromControlState(const FormControlState& state,
+                                          wtf_size_t& index);
+
  private:
   void SetEntry(const Entry*);
   IterationSource* StartIteration(ScriptState*, ExceptionState&) override;
@@ -130,7 +135,7 @@
   const String& name() const { return name_; }
   const String& Value() const { return value_; }
   Blob* GetBlob() const { return blob_.Get(); }
-  File* GetFile() const;
+  CORE_EXPORT File* GetFile() const;
   const String& Filename() const { return filename_; }
 
  private:
diff --git a/third_party/blink/renderer/core/html/forms/form_data_test.cc b/third_party/blink/renderer/core/html/forms/form_data_test.cc
index 03bb2f6..4638eb2 100644
--- a/third_party/blink/renderer/core/html/forms/form_data_test.cc
+++ b/third_party/blink/renderer/core/html/forms/form_data_test.cc
@@ -5,9 +5,22 @@
 #include "third_party/blink/renderer/core/html/forms/form_data.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/fileapi/file.h"
+#include "third_party/blink/renderer/core/html/forms/form_controller.h"
 
 namespace blink {
 
+namespace {
+
+FormData* Deserialize(const Vector<String>& strings) {
+  wtf_size_t i = 0;
+  auto state = FormControlState::Deserialize(strings, i);
+  wtf_size_t j = 0;
+  return FormData::CreateFromControlState(state, j);
+}
+
+}  // namespace
+
 TEST(FormDataTest, append) {
   FormData* fd = FormData::Create(UTF8Encoding());
   fd->append("test\n1", "value\n1");
@@ -73,4 +86,76 @@
   EXPECT_EQ(1u, fd->size());
 }
 
+TEST(FormDataTest, AppendToControlState) {
+  {
+    auto* fd = MakeGarbageCollected<FormData>();
+    FormControlState state;
+    fd->AppendToControlState(state);
+
+    EXPECT_EQ(1u, state.ValueSize());
+    EXPECT_EQ("0", state[0]) << "Number of entries should be 0";
+  }
+
+  {
+    auto* fd = MakeGarbageCollected<FormData>();
+    fd->append("n1", "string");
+    fd->AppendFromElement("n1", File::Create("/etc/hosts"));
+    FormControlState state;
+    fd->AppendToControlState(state);
+
+    EXPECT_EQ(9u, state.ValueSize());
+    EXPECT_EQ("2", state[0]) << "Number of entries should be 2";
+
+    EXPECT_EQ("n1", state[1]);
+    EXPECT_EQ("USVString", state[2]);
+    EXPECT_EQ("string", state[3]);
+
+    EXPECT_EQ("n1", state[4]);
+    EXPECT_EQ("File", state[5]);
+    EXPECT_EQ("/etc/hosts", state[6]);
+    EXPECT_EQ("hosts", state[7]);
+    EXPECT_EQ(String(), state[8]);
+  }
+}
+
+TEST(FormDataTest, CreateFromControlState) {
+  EXPECT_EQ(nullptr, Deserialize({"1", "not-a-number"}))
+      << "Should fail on size parsing";
+
+  auto* fd0 = Deserialize({"1", "0"});
+  ASSERT_NE(nullptr, fd0);
+  EXPECT_EQ(0u, fd0->size());
+
+  EXPECT_EQ(nullptr, Deserialize({"1", "1"})) << "Missing name value";
+
+  EXPECT_EQ(nullptr, Deserialize({"2", "1", "n0"})) << "Missing entry type";
+
+  EXPECT_EQ(nullptr, Deserialize({"3", "1", "n0", "DOMString"}))
+      << "Unknown entry type";
+
+  EXPECT_EQ(nullptr, Deserialize({"3", "1", "n0", "USVString"}))
+      << "Missing USVString value";
+
+  EXPECT_EQ(nullptr, Deserialize({"3", "1", "n1", "File"}))
+      << "Missing File value 1";
+
+  EXPECT_EQ(nullptr, Deserialize({"4", "1", "n1", "File", "/etc/hosts"}))
+      << "Missing File value 2";
+
+  EXPECT_EQ(nullptr,
+            Deserialize({"5", "1", "n1", "File", "/etc/password", "pasword"}))
+      << "Missing File value 3";
+
+  auto* fd = Deserialize({"9", "2", "n1", "USVString", "string-value", "n2",
+                          "File", "/etc/password", "pasword", ""});
+  ASSERT_NE(nullptr, fd);
+  EXPECT_EQ(2u, fd->size());
+  const FormData::Entry* entry0 = fd->Entries()[0];
+  EXPECT_TRUE(entry0->IsString());
+  EXPECT_EQ("string-value", entry0->Value());
+  const FormData::Entry* entry1 = fd->Entries()[1];
+  EXPECT_TRUE(entry1->isFile());
+  EXPECT_EQ("/etc/password", entry1->GetFile()->GetPath());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_slot_element.h b/third_party/blink/renderer/core/html/html_slot_element.h
index e5dcacca..f8e5a77 100644
--- a/third_party/blink/renderer/core/html/html_slot_element.h
+++ b/third_party/blink/renderer/core/html/html_slot_element.h
@@ -139,8 +139,6 @@
 
   void SetNeedsDistributionRecalcWillBeSetNeedsAssignmentRecalc();
 
-  const HeapVector<Member<Node>>& GetDistributedNodes();
-
   void RecalcFlatTreeChildren();
   void UpdateFlatTreeNodeDataForAssignedNodes();
   void ClearAssignedNodesAndFlatTreeChildren();
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index 66e6653..36ac3da 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -184,6 +184,7 @@
   visitor->Trace(frame_);
   visitor->Trace(selection_controller_);
   visitor->Trace(capturing_mouse_events_element_);
+  visitor->Trace(capturing_subframe_element_);
   visitor->Trace(last_mouse_move_event_subframe_);
   visitor->Trace(last_scrollbar_under_mouse_);
   visitor->Trace(drag_target_);
@@ -209,6 +210,7 @@
   should_only_fire_drag_over_event_ = false;
   last_mouse_down_user_gesture_token_ = nullptr;
   capturing_mouse_events_element_ = nullptr;
+  capturing_subframe_element_ = nullptr;
   pointer_event_manager_->Clear();
   scroll_manager_->Clear();
   gesture_manager_->Clear();
@@ -638,9 +640,7 @@
   // invalidated by what happens when we dispatch the event.
   LayoutPoint document_point = frame_->View()->ConvertFromRootFrame(
       FlooredIntPoint(mouse_event.PositionInRootFrame()));
-  MouseEventWithHitTestResults mev =
-      frame_->GetDocument()->PerformMouseEventHitTest(request, document_point,
-                                                      mouse_event);
+  MouseEventWithHitTestResults mev = GetMouseEventTarget(request, mouse_event);
   if (!mev.InnerNode()) {
     mouse_event_manager_->InvalidateClick();
     return WebInputEventResult::kNotHandled;
@@ -661,8 +661,10 @@
     mouse_event_manager_->SetCapturesDragging(
         subframe->GetEventHandler().mouse_event_manager_->CapturesDragging());
     if (mouse_event_manager_->MousePressed() &&
-        mouse_event_manager_->CapturesDragging())
+        mouse_event_manager_->CapturesDragging()) {
       capturing_mouse_events_element_ = mev.InnerElement();
+      capturing_subframe_element_ = mev.InnerElement();
+    }
 
     mouse_event_manager_->InvalidateClick();
     return result;
@@ -841,7 +843,7 @@
         WebInputEvent::Modifiers::kRelativeMotionEvent)) {
     mouse_event_manager_->ClearDragHeuristicState();
     capturing_mouse_events_element_ = nullptr;
-    CaptureMouseEventsToWidget(false);
+    ReleaseMouseCaptureFromLocalRoot();
   }
 
   if (RuntimeEnabledFeatures::MiddleClickAutoscrollEnabled()) {
@@ -894,12 +896,10 @@
   // might actually be some other frame above this one at the specified
   // co-ordinate. So we must force the hit-test to fail, while still clearing
   // hover/active state.
-  if (force_leave) {
+  if (force_leave)
     frame_->GetDocument()->UpdateHoverActiveState(request, nullptr);
-  } else {
-    mev = event_handling_util::PerformMouseEventHitTest(frame_, request,
-                                                        mouse_event);
-  }
+  else
+    mev = GetMouseEventTarget(request, mouse_event);
 
   if (hovered_node_result)
     *hovered_node_result = mev.GetHitTestResult();
@@ -924,7 +924,7 @@
   WebInputEventResult event_result = WebInputEventResult::kNotHandled;
   bool is_remote_frame = false;
   LocalFrame* new_subframe = event_handling_util::GetTargetSubframe(
-      mev, capturing_mouse_events_element_.Get(), &is_remote_frame);
+      mev, capturing_mouse_events_element_, &is_remote_frame);
 
   // We want mouseouts to happen first, from the inside out.  First send a
   // move event to the last subframe so that it will fire mouseouts.
@@ -1012,16 +1012,18 @@
   mouse_event_manager_->HandleSvgPanIfNeeded(true);
 
   if (frame_set_being_resized_) {
-    CaptureMouseEventsToWidget(false);
-    return mouse_event_manager_->SetMousePositionAndDispatchMouseEvent(
-        EffectiveMouseEventTargetElement(frame_set_being_resized_.Get()),
-        String(), event_type_names::kMouseup, mouse_event);
+    WebInputEventResult result =
+        mouse_event_manager_->SetMousePositionAndDispatchMouseEvent(
+            EffectiveMouseEventTargetElement(frame_set_being_resized_.Get()),
+            String(), event_type_names::kMouseup, mouse_event);
+    ReleaseMouseCaptureFromLocalRoot();
+    return result;
   }
 
   if (last_scrollbar_under_mouse_) {
     mouse_event_manager_->InvalidateClick();
     last_scrollbar_under_mouse_->MouseUp(mouse_event);
-    CaptureMouseEventsToWidget(false);
+    ReleaseMouseCaptureFromLocalRoot();
     return DispatchMousePointerEvent(
         WebInputEvent::kPointerUp, mouse_event_manager_->GetElementUnderMouse(),
         String(), mouse_event, Vector<WebMouseEvent>(),
@@ -1030,12 +1032,9 @@
 
   // Mouse events simulated from touch should not hit-test again.
   DCHECK(!mouse_event.FromTouch());
-
   HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kRelease;
   HitTestRequest request(hit_type);
-  MouseEventWithHitTestResults mev =
-      event_handling_util::PerformMouseEventHitTest(frame_, request,
-                                                    mouse_event);
+  MouseEventWithHitTestResults mev = GetMouseEventTarget(request, mouse_event);
   LocalFrame* subframe = event_handling_util::GetTargetSubframe(
       mev, capturing_mouse_events_element_.Get());
   capturing_mouse_events_element_ = nullptr;
@@ -1071,8 +1070,7 @@
     event_result = mouse_event_manager_->HandleMouseReleaseEvent(mev);
 
   mouse_event_manager_->HandleMouseReleaseEventUpdateStates();
-  CaptureMouseEventsToWidget(false);
-
+  ReleaseMouseCaptureFromLocalRoot();
   return event_result;
 }
 
@@ -1209,6 +1207,7 @@
   scroll_manager_->StopAutoscroll();
   drag_target_ = nullptr;
   capturing_mouse_events_element_ = nullptr;
+  ReleaseMouseCaptureFromLocalRoot();
   should_only_fire_drag_over_event_ = false;
 }
 
@@ -1223,8 +1222,12 @@
 Element* EventHandler::EffectiveMouseEventTargetElement(
     Element* target_element) {
   Element* new_element_under_mouse = target_element;
-  if (capturing_mouse_events_element_) {
-    new_element_under_mouse = capturing_mouse_events_element_.Get();
+  if (RuntimeEnabledFeatures::UnifiedPointerCaptureInBlinkEnabled()) {
+    if (pointer_event_manager_->GetMouseCaptureTarget())
+      new_element_under_mouse = pointer_event_manager_->GetMouseCaptureTarget();
+  } else {
+    if (capturing_mouse_events_element_)
+      new_element_under_mouse = capturing_mouse_events_element_.Get();
   }
   return new_element_under_mouse;
 }
@@ -1275,7 +1278,7 @@
 }
 
 void EventHandler::ReleaseMousePointerCapture() {
-  pointer_event_manager_->ReleaseMousePointerCapture();
+  ReleaseMouseCaptureFromLocalRoot();
 }
 
 bool EventHandler::HasPointerCapture(PointerId pointer_id,
@@ -2227,4 +2230,68 @@
   is_widget_capturing_mouse_events_ = capture;
 }
 
+MouseEventWithHitTestResults EventHandler::GetMouseEventTarget(
+    const HitTestRequest& request,
+    const WebMouseEvent& event) {
+  LayoutPoint document_point = event_handling_util::ContentPointFromRootFrame(
+      frame_, event.PositionInRootFrame());
+
+  // TODO(eirage): This does not handle chorded buttons yet.
+  if (RuntimeEnabledFeatures::UnifiedPointerCaptureInBlinkEnabled() &&
+      event.GetType() != WebInputEvent::kMouseDown) {
+    HitTestResult result(request, HitTestLocation(document_point));
+
+    Element* capture_target;
+    if (event_handling_util::SubframeForTargetNode(
+            capturing_subframe_element_)) {
+      capture_target = capturing_subframe_element_;
+      result.SetIsOverEmbeddedContentView(true);
+    } else {
+      capture_target = pointer_event_manager_->GetMouseCaptureTarget();
+    }
+
+    if (capture_target) {
+      LayoutObject* layout_object = capture_target->GetLayoutObject();
+
+      LayoutPoint local_point =
+          layout_object ? LayoutPoint(layout_object->AbsoluteToLocal(
+                              FloatPoint(document_point)))
+                        : document_point;
+
+      result.SetNodeAndPosition(capture_target, local_point);
+
+      result.SetScrollbar(last_scrollbar_under_mouse_);
+      result.SetURLElement(capture_target->EnclosingLinkEventParentOrSelf());
+
+      if (!request.ReadOnly()) {
+        frame_->GetDocument()->UpdateHoverActiveState(request,
+                                                      result.InnerElement());
+      }
+
+      return MouseEventWithHitTestResults(
+          event, HitTestLocation(document_point), result);
+    }
+  }
+  return frame_->GetDocument()->PerformMouseEventHitTest(request,
+                                                         document_point, event);
+}
+
+void EventHandler::ReleaseMouseCaptureFromLocalRoot() {
+  CaptureMouseEventsToWidget(false);
+
+  if (RuntimeEnabledFeatures::UnifiedPointerCaptureInBlinkEnabled()) {
+    frame_->LocalFrameRoot()
+        .GetEventHandler()
+        .ReleaseMouseCaptureFromCurrentFrame();
+  }
+}
+
+void EventHandler::ReleaseMouseCaptureFromCurrentFrame() {
+  if (LocalFrame* subframe = event_handling_util::SubframeForTargetNode(
+          capturing_subframe_element_))
+    subframe->GetEventHandler().ReleaseMouseCaptureFromCurrentFrame();
+  pointer_event_manager_->ReleaseMousePointerCapture();
+  capturing_subframe_element_ = nullptr;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/input/event_handler.h b/third_party/blink/renderer/core/input/event_handler.h
index c401315..d1a9b3d 100644
--- a/third_party/blink/renderer/core/input/event_handler.h
+++ b/third_party/blink/renderer/core/input/event_handler.h
@@ -397,6 +397,13 @@
 
   void CaptureMouseEventsToWidget(bool);
 
+  void ReleaseMouseCaptureFromLocalRoot();
+  void ReleaseMouseCaptureFromCurrentFrame();
+
+  MouseEventWithHitTestResults GetMouseEventTarget(
+      const HitTestRequest& request,
+      const WebMouseEvent& mev);
+
   // NOTE: If adding a new field to this class please ensure that it is
   // cleared in |EventHandler::clear()|.
 
@@ -412,6 +419,10 @@
   TaskRunnerTimer<EventHandler> cursor_update_timer_;
 
   Member<Element> capturing_mouse_events_element_;
+  // |capturing_subframe_element_| has similar functionality as
+  // |capturing_mouse_events_element_|. It replaces |capturing_..| when
+  // UnifiedPointerCapture enabled.
+  Member<Element> capturing_subframe_element_;
 
   // Indicates whether the current widget is capturing mouse input.
   // Only used for local frame root EventHandlers.
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 3ef6e6c..6139ed7 100644
--- a/third_party/blink/renderer/core/input/event_handling_util.cc
+++ b/third_party/blink/renderer/core/input/event_handling_util.cc
@@ -12,6 +12,7 @@
 #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/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 namespace event_handling_util {
@@ -150,7 +151,8 @@
     const MouseEventWithHitTestResults& hit_test_result,
     Node* capturing_node,
     bool* is_remote_frame) {
-  if (capturing_node) {
+  if (!RuntimeEnabledFeatures::UnifiedPointerCaptureInBlinkEnabled() &&
+      capturing_node) {
     return event_handling_util::SubframeForTargetNode(capturing_node,
                                                       is_remote_frame);
   }
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 74199c5e..1883ce8 100644
--- a/third_party/blink/renderer/core/input/event_handling_util.h
+++ b/third_party/blink/renderer/core/input/event_handling_util.h
@@ -41,7 +41,7 @@
 ContainerNode* ParentForClickEvent(const Node&);
 
 LayoutPoint ContentPointFromRootFrame(LocalFrame*,
-                                      const IntPoint& point_in_root_frame);
+                                      const FloatPoint& point_in_root_frame);
 
 MouseEventWithHitTestResults PerformMouseEventHitTest(LocalFrame*,
                                                       const HitTestRequest&,
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 21f30c6..b84e3bd 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.cc
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/core/input/mouse_event_manager.h"
 #include "third_party/blink/renderer/core/input/touch_action_util.h"
 #include "third_party/blink/renderer/core/layout/hit_test_canvas_result.h"
+#include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/pointer_lock_controller.h"
@@ -745,9 +746,20 @@
     // Send got/lostpointercapture rightaway if necessary.
     if (pointer_event->type() == event_type_names::kPointerup) {
       // If pointerup releases the capture we also send boundary events
-      // rightaway when the pointer that supports hover. The following function
-      // does nothing when there was no capture to begin with in the first
-      // place.
+      // rightaway when the pointer that supports hover. Perform a hit
+      // test to find the new target.
+      if (RuntimeEnabledFeatures::UnifiedPointerCaptureInBlinkEnabled()) {
+        if (pointer_capture_target_.find(pointer_event->pointerId()) !=
+            pointer_capture_target_.end()) {
+          HitTestRequest::HitTestRequestType hit_type =
+              HitTestRequest::kRelease;
+          HitTestRequest request(hit_type);
+          MouseEventWithHitTestResults mev =
+              event_handling_util::PerformMouseEventHitTest(frame_, request,
+                                                            mouse_event);
+          target = mev.InnerElement();
+        }
+      }
       ProcessCaptureAndPositionOfPointerEvent(pointer_event, target,
                                               canvas_region_id, &mouse_event);
     } else {
@@ -935,6 +947,12 @@
   pending_pointer_capture_target_.erase(pointer_id);
 }
 
+Element* PointerEventManager::GetMouseCaptureTarget() {
+  if (pending_pointer_capture_target_.Contains(PointerEventFactory::kMouseId))
+    return pending_pointer_capture_target_.at(PointerEventFactory::kMouseId);
+  return nullptr;
+}
+
 bool PointerEventManager::IsActive(const PointerId pointer_id) const {
   return pointer_event_factory_.IsActive(pointer_id);
 }
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.h b/third_party/blink/renderer/core/input/pointer_event_manager.h
index 971f0d5..157e02e5 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.h
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.h
@@ -100,6 +100,8 @@
 
   void RemoveLastMousePosition();
 
+  Element* GetMouseCaptureTarget();
+
   // Sends any outstanding events. For example it notifies TouchEventManager
   // to group any changes to touch since last FlushEvents and send the touch
   // event out to js. Since after this function any outstanding event is sent,
diff --git a/third_party/blink/renderer/core/inspector/inspect_tools.cc b/third_party/blink/renderer/core/inspector/inspect_tools.cc
index 6d74893..80af051 100644
--- a/third_party/blink/renderer/core/inspector/inspect_tools.cc
+++ b/third_party/blink/renderer/core/inspector/inspect_tools.cc
@@ -9,11 +9,14 @@
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/public/platform/web_keyboard_event.h"
 #include "third_party/blink/public/platform/web_pointer_event.h"
+#include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
+#include "third_party/blink/renderer/core/dom/static_node_list.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/root_frame_viewport.h"
 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
+#include "third_party/blink/renderer/core/inspector/inspector_css_agent.h"
 #include "third_party/blink/renderer/core/inspector/inspector_dom_agent.h"
 #include "third_party/blink/renderer/core/layout/hit_test_location.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
@@ -84,14 +87,34 @@
   protocol::ErrorSupport errors;
   std::unique_ptr<protocol::Overlay::HighlightConfig> highlight_config =
       protocol::Overlay::HighlightConfig::fromValue(value.get(), &errors);
-  inspect_mode_highlight_config_ =
+  highlight_config_ =
       InspectorOverlayAgent::ToHighlightConfig(highlight_config.get());
 }
 
 void SearchingForNodeTool::Trace(blink::Visitor* visitor) {
   InspectTool::Trace(visitor);
   visitor->Trace(dom_agent_);
-  visitor->Trace(hovered_node_for_inspect_mode_);
+  visitor->Trace(hovered_node_);
+  visitor->Trace(event_target_node_);
+}
+
+void SearchingForNodeTool::Draw(float scale) {
+  Node* node = hovered_node_.Get();
+  if (!hovered_node_)
+    return;
+  bool append_element_info = (node->IsElementNode() || node->IsTextNode()) &&
+                             !omit_tooltip_ && highlight_config_->show_info &&
+                             node->GetLayoutObject() &&
+                             node->GetDocument().GetFrame();
+  InspectorHighlight highlight(node, *highlight_config_, contrast_info_,
+                               append_element_info);
+  if (event_target_node_) {
+    highlight.AppendEventTargetQuads(event_target_node_.Get(),
+                                     *highlight_config_);
+  }
+  std::unique_ptr<protocol::DictionaryValue> highlight_json =
+      highlight.AsProtocolValue();
+  overlay_->EvaluateInOverlay("drawHighlight", std::move(highlight_json));
 }
 
 bool SearchingForNodeTool::HandleMouseMove(const WebMouseEvent& event) {
@@ -119,34 +142,45 @@
     if (!IsA<LocalFrame>(frame_owner->ContentFrame())) {
       // Do not consume event so that remote frame can handle it.
       overlay_->hideHighlight();
-      hovered_node_for_inspect_mode_.Clear();
+      hovered_node_.Clear();
       return false;
     }
   }
 
-  Node* event_target = (event.GetModifiers() & WebInputEvent::kShiftKey)
+  // Store values for the highlight.
+  hovered_node_ = node;
+  event_target_node_ = (event.GetModifiers() & WebInputEvent::kShiftKey)
                            ? HoveredNodeForEvent(frame, event, false)
                            : nullptr;
-  if (event_target == node)
-    event_target = nullptr;
+  if (event_target_node_ == hovered_node_)
+    event_target_node_ = nullptr;
+  omit_tooltip_ = event.GetModifiers() &
+                  (WebInputEvent::kControlKey | WebInputEvent::kMetaKey);
 
-  if (node && inspect_mode_highlight_config_) {
-    hovered_node_for_inspect_mode_ = node;
-    NodeHighlightRequested(node);
-    bool omit_tooltip = event.GetModifiers() &
-                        (WebInputEvent::kControlKey | WebInputEvent::kMetaKey);
-    overlay_->InnerHighlightNode(node, event_target, String(),
-                                 *inspect_mode_highlight_config_, omit_tooltip);
+  if (node->IsElementNode()) {
+    // Compute the color contrast information here.
+    Vector<Color> bgcolors;
+    String font_size;
+    String font_weight;
+    InspectorCSSAgent::GetBackgroundColors(ToElement(node), &bgcolors,
+                                           &font_size, &font_weight);
+    if (bgcolors.size() == 1) {
+      contrast_info_.font_size = font_size;
+      contrast_info_.font_weight = font_weight;
+      contrast_info_.background_color = bgcolors[0];
+    }
   }
+
+  NodeHighlightRequested(node);
   return true;
 }
 
 bool SearchingForNodeTool::HandleMouseDown(const WebMouseEvent& event,
                                            bool* swallow_next_mouse_up) {
-  if (hovered_node_for_inspect_mode_) {
+  if (hovered_node_) {
     *swallow_next_mouse_up = true;
-    overlay_->Inspect(hovered_node_for_inspect_mode_.Get());
-    hovered_node_for_inspect_mode_.Clear();
+    overlay_->Inspect(hovered_node_.Get());
+    hovered_node_.Clear();
     return true;
   }
   return false;
@@ -154,9 +188,7 @@
 
 bool SearchingForNodeTool::HandleGestureTapEvent(const WebGestureEvent& event) {
   Node* node = HoveredNodeForEvent(overlay_->GetFrame(), event, false);
-  if (node && inspect_mode_highlight_config_) {
-    overlay_->InnerHighlightNode(node, nullptr, String(),
-                                 *inspect_mode_highlight_config_, false);
+  if (node) {
     overlay_->Inspect(node);
     return true;
   }
@@ -165,9 +197,7 @@
 
 bool SearchingForNodeTool::HandlePointerEvent(const WebPointerEvent& event) {
   Node* node = HoveredNodeForEvent(overlay_->GetFrame(), event, false);
-  if (node && inspect_mode_highlight_config_) {
-    overlay_->InnerHighlightNode(node, nullptr, String(),
-                                 *inspect_mode_highlight_config_, false);
+  if (node) {
     overlay_->Inspect(node);
     return true;
   }
@@ -187,6 +217,105 @@
     frontend_->nodeHighlightRequested(node_id);
 }
 
+// QuadHighlightTool -----------------------------------------------------------
+
+QuadHighlightTool::QuadHighlightTool(std::unique_ptr<FloatQuad> quad,
+                                     Color color,
+                                     Color outline_color)
+    : quad_(std::move(quad)), color_(color), outline_color_(outline_color) {}
+
+bool QuadHighlightTool::ForwardEventsToOverlay() {
+  return false;
+}
+
+void QuadHighlightTool::Draw(float scale) {
+  InspectorHighlight highlight(scale);
+  highlight.AppendQuad(*quad_, color_, outline_color_);
+  overlay_->EvaluateInOverlay("drawHighlight", highlight.AsProtocolValue());
+}
+
+// NodeHighlightTool -----------------------------------------------------------
+
+NodeHighlightTool::NodeHighlightTool(
+    Member<Node> node,
+    String selector_list,
+    std::unique_ptr<InspectorHighlightConfig> highlight_config)
+    : node_(node),
+      selector_list_(selector_list),
+      highlight_config_(std::move(highlight_config)) {
+  if (node->IsElementNode()) {
+    // Compute the color contrast information here.
+    Vector<Color> bgcolors;
+    String font_size;
+    String font_weight;
+    InspectorCSSAgent::GetBackgroundColors(ToElement(node), &bgcolors,
+                                           &font_size, &font_weight);
+    if (bgcolors.size() == 1) {
+      contrast_info_.font_size = font_size;
+      contrast_info_.font_weight = font_weight;
+      contrast_info_.background_color = bgcolors[0];
+    }
+  }
+}
+
+bool NodeHighlightTool::ForwardEventsToOverlay() {
+  return false;
+}
+
+void NodeHighlightTool::Draw(float scale) {
+  DrawNode();
+  DrawMatchingSelector();
+}
+
+void NodeHighlightTool::DrawNode() {
+  bool append_element_info = (node_->IsElementNode() || node_->IsTextNode()) &&
+                             highlight_config_->show_info &&
+                             node_->GetLayoutObject() &&
+                             node_->GetDocument().GetFrame();
+  InspectorHighlight highlight(node_.Get(), *highlight_config_, contrast_info_,
+                               append_element_info);
+  std::unique_ptr<protocol::DictionaryValue> highlight_json =
+      highlight.AsProtocolValue();
+  overlay_->EvaluateInOverlay("drawHighlight", std::move(highlight_json));
+}
+
+void NodeHighlightTool::DrawMatchingSelector() {
+  if (selector_list_.IsEmpty() || !node_)
+    return;
+  DummyExceptionStateForTesting exception_state;
+  ContainerNode* query_base = node_->ContainingShadowRoot();
+  if (!query_base)
+    query_base = node_->ownerDocument();
+  StaticElementList* elements = query_base->QuerySelectorAll(
+      AtomicString(selector_list_), exception_state);
+  if (exception_state.HadException())
+    return;
+
+  for (unsigned i = 0; i < elements->length(); ++i) {
+    Element* element = elements->item(i);
+    InspectorHighlight highlight(element, *highlight_config_, contrast_info_,
+                                 false);
+    std::unique_ptr<protocol::DictionaryValue> highlight_json =
+        highlight.AsProtocolValue();
+    overlay_->EvaluateInOverlay("drawHighlight", std::move(highlight_json));
+  }
+}
+
+void NodeHighlightTool::Trace(blink::Visitor* visitor) {
+  InspectTool::Trace(visitor);
+  visitor->Trace(node_);
+}
+
+// ShowViewSizeTool ------------------------------------------------------------
+
+void ShowViewSizeTool::Draw(float scale) {
+  overlay_->EvaluateInOverlay("drawViewSize", "");
+}
+
+bool ShowViewSizeTool::ForwardEventsToOverlay() {
+  return false;
+}
+
 // ScreenshotTool --------------------------------------------------------------
 
 void ScreenshotTool::DoInit() {
@@ -265,8 +394,9 @@
       overlay_->GetFrame()->GetPage()->GetVisualViewport();
   IntPoint p1 = visual_viewport.RootFrameToViewport(screenshot_anchor_);
   IntPoint p2 = visual_viewport.RootFrameToViewport(screenshot_position_);
-  p1.Scale(scale, scale);
-  p2.Scale(scale, scale);
+  float rscale = 1.f / scale;
+  p1.Scale(rscale, rscale);
+  p2.Scale(rscale, rscale);
   std::unique_ptr<protocol::DictionaryValue> data =
       protocol::DictionaryValue::create();
   data->setInteger("x1", p1.X());
diff --git a/third_party/blink/renderer/core/inspector/inspect_tools.h b/third_party/blink/renderer/core/inspector/inspect_tools.h
index 5389bf48..3fee38a2 100644
--- a/third_party/blink/renderer/core/inspector/inspect_tools.h
+++ b/third_party/blink/renderer/core/inspector/inspect_tools.h
@@ -13,6 +13,8 @@
 class WebMouseEvent;
 class WebPointerEvent;
 
+// -----------------------------------------------------------------------------
+
 class SearchingForNodeTool : public InspectTool {
  public:
   SearchingForNodeTool(InspectorDOMAgent* dom_agent,
@@ -25,15 +27,73 @@
   bool HandleMouseMove(const WebMouseEvent& event) override;
   bool HandleGestureTapEvent(const WebGestureEvent&) override;
   bool HandlePointerEvent(const WebPointerEvent&) override;
+  void Draw(float scale) override;
   void NodeHighlightRequested(Node*);
   void Trace(blink::Visitor* visitor) override;
 
   Member<InspectorDOMAgent> dom_agent_;
   bool ua_shadow_;
-  Member<Node> hovered_node_for_inspect_mode_;
-  std::unique_ptr<InspectorHighlightConfig> inspect_mode_highlight_config_;
+  Member<Node> hovered_node_;
+  Member<Node> event_target_node_;
+  std::unique_ptr<InspectorHighlightConfig> highlight_config_;
+  InspectorHighlightContrastInfo contrast_info_;
+  bool omit_tooltip_ = false;
+  DISALLOW_COPY_AND_ASSIGN(SearchingForNodeTool);
 };
 
+// -----------------------------------------------------------------------------
+
+class QuadHighlightTool : public InspectTool {
+ public:
+  QuadHighlightTool(std::unique_ptr<FloatQuad> quad,
+                    Color color,
+                    Color outline_color);
+
+ private:
+  bool ForwardEventsToOverlay() override;
+  void Draw(float scale) override;
+  std::unique_ptr<FloatQuad> quad_;
+  Color color_;
+  Color outline_color_;
+  DISALLOW_COPY_AND_ASSIGN(QuadHighlightTool);
+};
+
+// -----------------------------------------------------------------------------
+
+class NodeHighlightTool : public InspectTool {
+ public:
+  NodeHighlightTool(Member<Node> node,
+                    String selector_list,
+                    std::unique_ptr<InspectorHighlightConfig> highlight_config);
+
+ private:
+  bool ForwardEventsToOverlay() override;
+  void Draw(float scale) override;
+  void DrawNode();
+  void DrawMatchingSelector();
+  void Trace(blink::Visitor* visitor) override;
+
+  Member<Node> node_;
+  String selector_list_;
+  std::unique_ptr<InspectorHighlightConfig> highlight_config_;
+  InspectorHighlightContrastInfo contrast_info_;
+  DISALLOW_COPY_AND_ASSIGN(NodeHighlightTool);
+};
+
+// -----------------------------------------------------------------------------
+
+class ShowViewSizeTool : public InspectTool {
+ public:
+  ShowViewSizeTool() = default;
+
+ private:
+  bool ForwardEventsToOverlay() override;
+  void Draw(float scale) override;
+  DISALLOW_COPY_AND_ASSIGN(ShowViewSizeTool);
+};
+
+// -----------------------------------------------------------------------------
+
 class ScreenshotTool : public InspectTool {
  public:
   ScreenshotTool() = default;
@@ -50,8 +110,11 @@
 
   IntPoint screenshot_anchor_;
   IntPoint screenshot_position_;
+  DISALLOW_COPY_AND_ASSIGN(ScreenshotTool);
 };
 
+// -----------------------------------------------------------------------------
+
 class PausedInDebuggerTool : public InspectTool {
  public:
   explicit PausedInDebuggerTool(const String& message) : message_(message) {}
@@ -59,6 +122,7 @@
  private:
   void Draw(float scale) override;
   String message_;
+  DISALLOW_COPY_AND_ASSIGN(PausedInDebuggerTool);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index 58b36aa..49502ec 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -166,7 +166,7 @@
   void PaintFrameOverlay(const FrameOverlay& frame_overlay,
                          GraphicsContext& graphics_context,
                          const IntSize&) const override {
-    if (overlay_->IsEmpty())
+    if (!overlay_->inspect_tool_)
       return;
 
     if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
@@ -256,7 +256,6 @@
     : frame_impl_(frame_impl),
       inspected_frames_(inspected_frames),
       resize_timer_active_(false),
-      omit_tooltip_(false),
       timer_(
           frame_impl->GetFrame()->GetTaskRunner(TaskType::kInternalInspector),
           this,
@@ -288,8 +287,6 @@
 void InspectorOverlayAgent::Trace(blink::Visitor* visitor) {
   visitor->Trace(frame_impl_);
   visitor->Trace(inspected_frames_);
-  visitor->Trace(highlight_node_);
-  visitor->Trace(event_target_node_);
   visitor->Trace(overlay_page_);
   visitor->Trace(overlay_chrome_client_);
   visitor->Trace(overlay_host_);
@@ -464,8 +461,9 @@
     Maybe<protocol::DOM::RGBA> outline_color) {
   std::unique_ptr<FloatQuad> quad =
       std::make_unique<FloatQuad>(FloatRect(x, y, width, height));
-  InnerHighlightQuad(std::move(quad), std::move(color),
-                     std::move(outline_color));
+  SetInspectTool(MakeGarbageCollected<QuadHighlightTool>(
+      std::move(quad), InspectorDOMAgent::ParseColor(color.fromMaybe(nullptr)),
+      InspectorDOMAgent::ParseColor(outline_color.fromMaybe(nullptr))));
   return Response::OK();
 }
 
@@ -476,8 +474,9 @@
   std::unique_ptr<FloatQuad> quad = std::make_unique<FloatQuad>();
   if (!ParseQuad(std::move(quad_array), quad.get()))
     return Response::Error("Invalid Quad format");
-  InnerHighlightQuad(std::move(quad), std::move(color),
-                     std::move(outline_color));
+  SetInspectTool(MakeGarbageCollected<QuadHighlightTool>(
+      std::move(quad), InspectorDOMAgent::ParseColor(color.fromMaybe(nullptr)),
+      InspectorDOMAgent::ParseColor(outline_color.fromMaybe(nullptr))));
   return Response::OK();
 }
 
@@ -500,8 +499,8 @@
   if (!response.isSuccess())
     return response;
 
-  InnerHighlightNode(node, nullptr, selector_list.fromMaybe(String()),
-                     *highlight_config, false);
+  SetInspectTool(MakeGarbageCollected<NodeHighlightTool>(
+      node, selector_list.fromMaybe(String()), std::move(highlight_config)));
   return Response::OK();
 }
 
@@ -520,15 +519,15 @@
         InspectorDOMAgent::ParseColor(color.fromMaybe(nullptr));
     highlight_config->content_outline =
         InspectorDOMAgent::ParseColor(outline_color.fromMaybe(nullptr));
-    InnerHighlightNode(frame->DeprecatedLocalOwner(), nullptr, String(),
-                       *highlight_config, false);
+
+    SetInspectTool(MakeGarbageCollected<NodeHighlightTool>(
+        frame->DeprecatedLocalOwner(), String(), std::move(highlight_config)));
   }
   return Response::OK();
 }
 
 Response InspectorOverlayAgent::hideHighlight() {
-  InnerHideHighlight();
-  ScheduleUpdate();
+  PickTheRightTool();
   return Response::OK();
 }
 
@@ -546,7 +545,7 @@
 }
 
 void InspectorOverlayAgent::Invalidate() {
-  if (IsEmpty())
+  if (!inspect_tool_)
     return;
 
   if (!frame_overlay_) {
@@ -563,7 +562,7 @@
   if (frame_overlay_)
     frame_overlay_->Update();
 
-  if (!IsEmpty()) {
+  if (inspect_tool_) {
     base::AutoReset<bool> scoped(&in_layout_, true);
     if (needs_update_) {
       needs_update_ = false;
@@ -600,7 +599,7 @@
 }
 
 void InspectorOverlayAgent::DispatchBufferedTouchEvents() {
-  if (IsEmpty())
+  if (!inspect_tool_)
     return;
   OverlayMainFrame()->GetEventHandler().DispatchBufferedTouchEvents();
 }
@@ -622,7 +621,7 @@
     }
   }
 
-  if (IsEmpty() || !inspect_tool_)
+  if (!inspect_tool_)
     return WebInputEventResult::kNotHandled;
 
   if (input_event.GetType() == WebInputEvent::kGestureTap) {
@@ -757,67 +756,9 @@
   return OverlayMainFrame()->GetEventHandler().HandleWheelEvent(wheel_event);
 }
 
-void InspectorOverlayAgent::InnerHideHighlight() {
-  highlight_node_.Clear();
-  event_target_node_.Clear();
-  highlight_quad_.reset();
-  highlight_node_contrast_ = InspectorHighlightContrastInfo();
-}
-
-void InspectorOverlayAgent::InnerHighlightNode(
-    Node* node,
-    Node* event_target,
-    String selector_list,
-    const InspectorHighlightConfig& highlight_config,
-    bool omit_tooltip) {
-  node_highlight_config_ = highlight_config;
-  highlight_node_ = node;
-  highlight_selector_list_ = selector_list;
-  event_target_node_ = event_target;
-  omit_tooltip_ = omit_tooltip;
-  highlight_node_contrast_ = InspectorHighlightContrastInfo();
-
-  if (node->IsElementNode()) {
-    // Compute the color contrast information here.
-    Vector<Color> bgcolors;
-    String font_size;
-    String font_weight;
-    InspectorCSSAgent::GetBackgroundColors(ToElement(node), &bgcolors,
-                                           &font_size, &font_weight);
-    if (bgcolors.size() == 1) {
-      highlight_node_contrast_.font_size = font_size;
-      highlight_node_contrast_.font_weight = font_weight;
-      highlight_node_contrast_.background_color = bgcolors[0];
-    }
-  }
-
-  ScheduleUpdate();
-}
-
-void InspectorOverlayAgent::InnerHighlightQuad(
-    std::unique_ptr<FloatQuad> quad,
-    Maybe<protocol::DOM::RGBA> color,
-    Maybe<protocol::DOM::RGBA> outline_color) {
-  quad_content_color_ = InspectorDOMAgent::ParseColor(color.fromMaybe(nullptr));
-  quad_content_outline_color_ =
-      InspectorDOMAgent::ParseColor(outline_color.fromMaybe(nullptr));
-  highlight_quad_ = std::move(quad);
-  omit_tooltip_ = false;
-  ScheduleUpdate();
-}
-
-bool InspectorOverlayAgent::IsEmpty() {
-  if (disposed_)
-    return true;
-  bool has_visible_elements =
-      highlight_node_ || event_target_node_ || highlight_quad_ ||
-      (resize_timer_active_ && show_size_on_resize_.Get());
-  return !has_visible_elements && !inspect_tool_;
-}
-
 void InspectorOverlayAgent::ScheduleUpdate() {
   auto& client = GetFrame()->GetPage()->GetChromeClient();
-  if (IsEmpty()) {
+  if (!inspect_tool_) {
     if (frame_overlay_) {
       frame_overlay_.reset();
       client.SetCursorOverridden(false);
@@ -843,12 +784,8 @@
   OverlayMainFrame()->SetPageZoomFactor(WindowToViewportScale());
 
   Reset(viewport_size);
-  DrawMatchingSelector();
-  DrawNodeHighlight();
-  DrawQuadHighlight();
-  DrawViewSize();
   if (inspect_tool_)
-    inspect_tool_->Draw(1.f / WindowToViewportScale());
+    inspect_tool_->Draw(WindowToViewportScale());
 }
 
 static std::unique_ptr<protocol::DictionaryValue> BuildObjectForSize(
@@ -860,64 +797,6 @@
   return result;
 }
 
-void InspectorOverlayAgent::DrawMatchingSelector() {
-  if (highlight_selector_list_.IsEmpty() || !highlight_node_)
-    return;
-  DummyExceptionStateForTesting exception_state;
-  ContainerNode* query_base = highlight_node_->ContainingShadowRoot();
-  if (!query_base)
-    query_base = highlight_node_->ownerDocument();
-  StaticElementList* elements = query_base->QuerySelectorAll(
-      AtomicString(highlight_selector_list_), exception_state);
-  if (exception_state.HadException())
-    return;
-
-  for (unsigned i = 0; i < elements->length(); ++i) {
-    Element* element = elements->item(i);
-    InspectorHighlight highlight(element, node_highlight_config_,
-                                 highlight_node_contrast_, false);
-    std::unique_ptr<protocol::DictionaryValue> highlight_json =
-        highlight.AsProtocolValue();
-    EvaluateInOverlay("drawHighlight", std::move(highlight_json));
-  }
-}
-
-void InspectorOverlayAgent::DrawNodeHighlight() {
-  if (!highlight_node_)
-    return;
-
-  bool append_element_info =
-      (highlight_node_->IsElementNode() || highlight_node_->IsTextNode()) &&
-      !omit_tooltip_ && node_highlight_config_.show_info &&
-      highlight_node_->GetLayoutObject() &&
-      highlight_node_->GetDocument().GetFrame();
-  InspectorHighlight highlight(highlight_node_.Get(), node_highlight_config_,
-                               highlight_node_contrast_, append_element_info);
-  if (event_target_node_) {
-    highlight.AppendEventTargetQuads(event_target_node_.Get(),
-                                     node_highlight_config_);
-  }
-
-  std::unique_ptr<protocol::DictionaryValue> highlight_json =
-      highlight.AsProtocolValue();
-  EvaluateInOverlay("drawHighlight", std::move(highlight_json));
-}
-
-void InspectorOverlayAgent::DrawQuadHighlight() {
-  if (!highlight_quad_)
-    return;
-
-  InspectorHighlight highlight(WindowToViewportScale());
-  highlight.AppendQuad(*highlight_quad_, quad_content_color_,
-                       quad_content_outline_color_);
-  EvaluateInOverlay("drawHighlight", highlight.AsProtocolValue());
-}
-
-void InspectorOverlayAgent::DrawViewSize() {
-  if (resize_timer_active_ && show_size_on_resize_.Get())
-    EvaluateInOverlay("drawViewSize", "");
-}
-
 float InspectorOverlayAgent::WindowToViewportScale() const {
   LocalFrame* frame = GetFrame();
   if (!frame)
@@ -1075,7 +954,7 @@
 
 void InspectorOverlayAgent::OnTimer(TimerBase*) {
   resize_timer_active_ = false;
-  ScheduleUpdate();
+  PickTheRightTool();
 }
 
 void InspectorOverlayAgent::OverlayResumed() {
@@ -1092,6 +971,8 @@
   if (resized && show_size_on_resize_.Get()) {
     resize_timer_active_ = true;
     timer_.StartOneShot(TimeDelta::FromSeconds(1), FROM_HERE);
+    PickTheRightTool();
+    return;
   }
   ScheduleUpdate();
 }
@@ -1175,10 +1056,13 @@
   } else if (!paused_in_debugger_message_.Get().IsNull()) {
     inspect_tool = MakeGarbageCollected<PausedInDebuggerTool>(
         paused_in_debugger_message_.Get());
+  } else if (resize_timer_active_ && show_size_on_resize_.Get()) {
+    inspect_tool = MakeGarbageCollected<ShowViewSizeTool>();
   }
+  SetInspectTool(inspect_tool);
+}
 
-  // Setting inspect tool clears existing highlight.
-  InnerHideHighlight();
+void InspectorOverlayAgent::SetInspectTool(InspectTool* inspect_tool) {
   if (inspect_tool_)
     inspect_tool_->Dispose();
   inspect_tool_ = inspect_tool;
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
index 1e3013e..a6ebe31 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
@@ -54,7 +54,6 @@
 
 namespace blink {
 
-class Color;
 class GraphicsContext;
 class InspectedFrames;
 class InspectorDOMAgent;
@@ -174,11 +173,6 @@
 
   LocalFrame* GetFrame() const;
   float WindowToViewportScale() const;
-  void InnerHighlightNode(Node*,
-                          Node* event_target,
-                          String selector,
-                          const InspectorHighlightConfig&,
-                          bool omit_tooltip);
 
  private:
   class InspectorOverlayChromeClient;
@@ -189,10 +183,6 @@
   void OverlaySteppedOver() override;
 
   bool IsEmpty();
-  void DrawMatchingSelector();
-  void DrawNodeHighlight();
-  void DrawQuadHighlight();
-  void DrawViewSize();
 
   Page* OverlayPage();
   LocalFrame* OverlayMainFrame();
@@ -205,13 +195,15 @@
   protocol::Response CompositingEnabled();
 
   bool InSomeInspectMode();
-  void InnerHighlightQuad(std::unique_ptr<FloatQuad>,
-                          protocol::Maybe<protocol::DOM::RGBA> color,
-                          protocol::Maybe<protocol::DOM::RGBA> outline_color);
-  void InnerHideHighlight();
+  void InnerHighlightNode(
+      Node*,
+      Node* event_target,
+      String selector,
+      std::unique_ptr<InspectorHighlightConfig> highlight_config);
 
   void SetNeedsUnbufferedInput(bool unbuffered);
   void PickTheRightTool();
+  void SetInspectTool(InspectTool* inspect_tool);
   protocol::Response HighlightConfigFromInspectorObject(
       protocol::Maybe<protocol::Overlay::HighlightConfig>
           highlight_inspector_object,
@@ -227,19 +219,10 @@
 
   Member<WebLocalFrameImpl> frame_impl_;
   Member<InspectedFrames> inspected_frames_;
-  Member<Node> highlight_node_;
-  String highlight_selector_list_;
-  InspectorHighlightContrastInfo highlight_node_contrast_;
-  Member<Node> event_target_node_;
-  InspectorHighlightConfig node_highlight_config_;
-  std::unique_ptr<FloatQuad> highlight_quad_;
   Member<Page> overlay_page_;
   Member<InspectorOverlayChromeClient> overlay_chrome_client_;
   Member<InspectorOverlayHost> overlay_host_;
-  Color quad_content_color_;
-  Color quad_content_outline_color_;
   bool resize_timer_active_;
-  bool omit_tooltip_;
   TaskRunnerTimer<InspectorOverlayAgent> timer_;
   bool disposed_;
   bool in_layout_;
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index a25c619e..3d88e8a 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -530,6 +530,14 @@
   return NextInPreOrderAfterChildren(stay_within);
 }
 
+LayoutObject* LayoutObject::PreviousInPostOrder(
+    const LayoutObject* stay_within) const {
+  if (LayoutObject* o = SlowLastChild())
+    return o;
+
+  return PreviousInPostOrderBeforeChildren(stay_within);
+}
+
 LayoutObject* LayoutObject::NextInPreOrderAfterChildren(
     const LayoutObject* stay_within) const {
   if (this == stay_within)
@@ -545,6 +553,21 @@
   return next;
 }
 
+LayoutObject* LayoutObject::PreviousInPostOrderBeforeChildren(
+    const LayoutObject* stay_within) const {
+  if (this == stay_within)
+    return nullptr;
+
+  const LayoutObject* current = this;
+  LayoutObject* previous = current->PreviousSibling();
+  for (; !previous; previous = current->PreviousSibling()) {
+    current = current->Parent();
+    if (!current || current == stay_within)
+      return nullptr;
+  }
+  return previous;
+}
+
 LayoutObject* LayoutObject::PreviousInPreOrder() const {
   if (LayoutObject* o = PreviousSibling()) {
     while (LayoutObject* last_child = o->SlowLastChild())
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 1368cd3..208feaf 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -314,9 +314,18 @@
   LayoutObject* NextInPreOrderAfterChildren() const;
   LayoutObject* NextInPreOrderAfterChildren(
       const LayoutObject* stay_within) const;
+
+  // Traverse in the exact reverse of the preorder traversal. In order words,
+  // they traverse in the last child -> first child -> root ordering.
   LayoutObject* PreviousInPreOrder() const;
   LayoutObject* PreviousInPreOrder(const LayoutObject* stay_within) const;
 
+  // Traverse in the exact reverse of the postorder traversal. In other words,
+  // they traverse in the root -> last child -> first child ordering.
+  LayoutObject* PreviousInPostOrder(const LayoutObject* stay_within) const;
+  LayoutObject* PreviousInPostOrderBeforeChildren(
+      const LayoutObject* stay_within) const;
+
   LayoutObject* LastLeafChild() const;
 
   // The following functions are used when the layout tree hierarchy changes to
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 d77a4b5f..818ef3f 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
@@ -257,12 +257,17 @@
   const NGConstraintSpace& old_space =
       cached_layout_result->GetConstraintSpaceForCaching();
 
-  // Check BFC block offset. Even if they don't match, there're some cases we
-  // can still reuse the fragment.
+  // Check the BFC offset. Even if they don't match, there're some cases we can
+  // still reuse the fragment.
   base::Optional<LayoutUnit> bfc_block_offset =
       cached_layout_result->BfcBlockOffset();
-  if (new_space.BfcOffset().block_offset !=
-      old_space.BfcOffset().block_offset) {
+  LayoutUnit bfc_line_offset = new_space.BfcOffset().line_offset;
+
+  DCHECK_EQ(old_space.BfcOffset().line_offset,
+            cached_layout_result->BfcLineOffset());
+
+  bool is_bfc_offset_equal = new_space.BfcOffset() == old_space.BfcOffset();
+  if (!is_bfc_offset_equal) {
     // Earlier floats may affect this box if block offset changes.
     if (new_space.HasFloats() || old_space.HasFloats())
       return nullptr;
@@ -291,8 +296,11 @@
   // The checks above should be enough to bail if layout is incomplete, but
   // let's verify:
   DCHECK(IsBlockLayoutComplete(old_space, *cached_layout_result));
-  return base::AdoptRef(
-      new NGLayoutResult(*cached_layout_result, bfc_block_offset));
+  if (is_bfc_offset_equal)
+    return cached_layout_result;
+
+  return base::AdoptRef(new NGLayoutResult(*cached_layout_result,
+                                           bfc_line_offset, bfc_block_offset));
 }
 
 template <typename Base>
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc
index 1505b6bf06..00bf9c4 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc
@@ -388,6 +388,42 @@
   EXPECT_EQ(result.get(), nullptr);
 }
 
+TEST_F(NGBlockLayoutAlgorithmTest, LineOffsetCaching) {
+  ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+  SetBodyInnerHTML(R"HTML(
+    <div id="container" style="display: flow-root; width: 300px; height: 100px;">
+      <div id="box1" style="width: 100px; margin: 0 auto 0 auto;"></div>
+    </div>
+  )HTML");
+
+  auto create_space = [&](auto size, auto bfc_offset) -> NGConstraintSpace {
+    return NGConstraintSpaceBuilder(WritingMode::kHorizontalTb,
+                                    WritingMode::kHorizontalTb,
+                                    /* is_new_formatting_context */ false)
+        .SetAvailableSize(size)
+        .SetPercentageResolutionSize(size)
+        .SetTextDirection(TextDirection::kLtr)
+        .AddBaselineRequest({NGBaselineAlgorithmType::kAtomicInline,
+                             FontBaseline::kAlphabeticBaseline})
+        .AddBaselineRequest({NGBaselineAlgorithmType::kFirstLine,
+                             FontBaseline::kAlphabeticBaseline})
+        .SetBfcOffset(bfc_offset)
+        .ToConstraintSpace();
+  };
+
+  NGConstraintSpace space200 =
+      create_space(NGLogicalSize(LayoutUnit(300), LayoutUnit(100)),
+                   NGBfcOffset(LayoutUnit(50), LayoutUnit()));
+
+  scoped_refptr<const NGLayoutResult> result;
+  LayoutBlockFlow* box1 = ToLayoutBlockFlow(GetLayoutObjectByElementId("box1"));
+
+  // Ensure we get a cached layout result, even if our BFC line-offset changed.
+  result = box1->CachedLayoutResult(space200, nullptr);
+  EXPECT_NE(result.get(), nullptr);
+}
+
 // Verifies that two children are laid out with the correct size and position.
 TEST_F(NGBlockLayoutAlgorithmTest, LayoutBlockChildren) {
   SetBodyInnerHTML(R"HTML(
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 3c56710..6442fffe 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
@@ -100,20 +100,6 @@
       .ToConstraintSpace();
 }
 
-bool NGConstraintSpace::operator==(const NGConstraintSpace& other) const {
-  if (!AreSizesEqual(other))
-    return false;
-
-  if (!MaySkipLayout(other))
-    return false;
-
-  if (!HasRareData() && !other.HasRareData() &&
-      bfc_offset_.block_offset != other.bfc_offset_.block_offset)
-    return false;
-
-  return true;
-}
-
 String NGConstraintSpace::ToString() const {
   return String::Format("Offset: %s,%s Size: %sx%s Clearance: %s",
                         bfc_offset_.line_offset.ToString().Ascii().data(),
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
index 6e042ea..4d4d616 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
@@ -424,10 +424,7 @@
     if (HasRareData() && other.HasRareData()) {
       if (!rare_data_->MaySkipLayout(*other.rare_data_))
         return false;
-    } else if (!HasRareData() && !other.HasRareData()) {
-      if (bfc_offset_.line_offset != other.bfc_offset_.line_offset)
-        return false;
-    } else {
+    } else if (HasRareData() != other.HasRareData()) {
       // We have a bfc_offset_, and a rare_data_ (or vice-versa).
       return false;
     }
@@ -482,10 +479,6 @@
 
     return true;
   }
-  bool operator==(const NGConstraintSpace&) const;
-  bool operator!=(const NGConstraintSpace& other) const {
-    return !(*this == other);
-  }
 
   String ToString() const;
 
@@ -532,7 +525,6 @@
 
     bool MaySkipLayout(const RareData& other) const {
       return margin_strut == other.margin_strut &&
-             bfc_offset.line_offset == other.bfc_offset.line_offset &&
              floats_bfc_block_offset == other.floats_bfc_block_offset &&
              clearance_offset == other.clearance_offset &&
              fragmentainer_block_size == other.fragmentainer_block_size &&
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
index 00948a9..87342a0b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
@@ -46,15 +46,15 @@
       << "Use the other constructor for successful layout";
 }
 
-// We can't use =default here because RefCounted can't be copied.
 NGLayoutResult::NGLayoutResult(const NGLayoutResult& other,
+                               LayoutUnit bfc_line_offset,
                                base::Optional<LayoutUnit> bfc_block_offset)
     : space_(other.space_),
       physical_fragment_(other.physical_fragment_),
       oof_positioned_descendants_(other.oof_positioned_descendants_),
       unpositioned_list_marker_(other.unpositioned_list_marker_),
       exclusion_space_(other.exclusion_space_),
-      bfc_line_offset_(other.bfc_line_offset_),
+      bfc_line_offset_(bfc_line_offset),
       bfc_block_offset_(bfc_block_offset),
       end_margin_strut_(other.end_margin_strut_),
       intrinsic_block_size_(other.intrinsic_block_size_),
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
index 1acb434..c7865c0 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
@@ -44,6 +44,7 @@
   // parameter. Note, when |bfc_block_offset| is |nullopt|, |BfcBlockOffset| is
   // still replaced with |nullopt|.
   NGLayoutResult(const NGLayoutResult&,
+                 LayoutUnit bfc_line_offset,
                  base::Optional<LayoutUnit> bfc_block_offset);
   ~NGLayoutResult();
 
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index 0f004cc..6980c49 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -34,6 +34,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/debug/alias.h"
 #include "base/optional.h"
 #include "build/build_config.h"
 #include "cc/animation/animation_host.h"
@@ -408,6 +409,24 @@
   WebFrameWidgetBase* widget = web_frame->LocalRootFrameWidget();
   if (!widget)
     return;
+
+  if (closed_) {
+    // TODO(crbug.com/939262): The main frame's WebWidget is closed, and all
+    // frames should be detached, so we should not be trying to animate them!
+    bool is_local_root_main_frame = frame.LocalFrameRoot().IsMainFrame();
+    base::debug::Alias(&is_local_root_main_frame);
+    bool is_main_frame = frame.IsMainFrame();
+    base::debug::Alias(&is_main_frame);
+    // If this fails the frame's document wasn't shutdown even though the main
+    // frame is detached.
+    CHECK(frame.GetDocument()->IsActive());
+    // If this fails the frame is detached but the document is active, which is
+    // unexpected.
+    CHECK(frame.IsAttached());
+    // If this fails the frame is provisional but has a WebFrameWidget.
+    CHECK(!frame.IsProvisional());
+  }
+
   // LocalRootFrameWidget() is a WebWidget, its client is the embedder.
   WebWidgetClient* web_widget_client = widget->Client();
   // TODO(crbug.com/939262): This shouldn't be null. The WebFrameWidget is
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.h b/third_party/blink/renderer/core/page/chrome_client_impl.h
index baccaaf..61633e6e 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.h
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.h
@@ -246,6 +246,10 @@
                      const PaintImage&,
                      base::OnceCallback<void(bool)>) override;
 
+  // TODO(crbug.com/939262): Track use of ChromeClientImpl after the frame's
+  // WebWidget is closed.
+  void SetClosed() { closed_ = true; }
+
  private:
   bool IsChromeClientImpl() const override { return true; }
 
@@ -261,6 +265,9 @@
   Cursor last_set_mouse_cursor_for_testing_;
   bool cursor_overridden_;
   bool did_request_non_empty_tool_tip_;
+  // TODO(crbug.com/939262): Track use of ChromeClientImpl after the frame's
+  // WebWidget is closed.
+  bool closed_ = false;
 
   FRIEND_TEST_ALL_PREFIXES(FileChooserQueueTest, DerefQueuedChooser);
 };
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index 66e5c7e..70c4672 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -234,7 +234,7 @@
 std::unique_ptr<GraphicsLayer> CompositedLayerMapping::CreateGraphicsLayer(
     CompositingReasons reasons,
     SquashingDisallowedReasons squashing_disallowed_reasons) {
-  std::unique_ptr<GraphicsLayer> graphics_layer = GraphicsLayer::Create(*this);
+  auto graphics_layer = std::make_unique<GraphicsLayer>(*this);
 
   graphics_layer->SetCompositingReasons(reasons);
   graphics_layer->SetSquashingDisallowedReasons(squashing_disallowed_reasons);
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
index 1ced4d2a..3b3bccc1 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
@@ -226,30 +226,26 @@
 
 bool CompositingReasonFinder::RequiresCompositingForOpacityAnimation(
     const ComputedStyle& style) {
-  return style.SubtreeWillChangeContents()
-             ? style.IsRunningOpacityAnimationOnCompositor()
-             : style.HasCurrentOpacityAnimation();
+  return style.HasCurrentOpacityAnimation() &&
+         !style.SubtreeWillChangeContents();
 }
 
 bool CompositingReasonFinder::RequiresCompositingForFilterAnimation(
     const ComputedStyle& style) {
-  return style.SubtreeWillChangeContents()
-             ? style.IsRunningFilterAnimationOnCompositor()
-             : style.HasCurrentFilterAnimation();
+  return style.HasCurrentFilterAnimation() &&
+         !style.SubtreeWillChangeContents();
 }
 
 bool CompositingReasonFinder::RequiresCompositingForBackdropFilterAnimation(
     const ComputedStyle& style) {
-  return style.SubtreeWillChangeContents()
-             ? style.IsRunningBackdropFilterAnimationOnCompositor()
-             : style.HasCurrentBackdropFilterAnimation();
+  return style.HasCurrentBackdropFilterAnimation() &&
+         !style.SubtreeWillChangeContents();
 }
 
 bool CompositingReasonFinder::RequiresCompositingForTransformAnimation(
     const ComputedStyle& style) {
-  return style.SubtreeWillChangeContents()
-             ? style.IsRunningTransformAnimationOnCompositor()
-             : style.HasCurrentTransformAnimation();
+  return style.HasCurrentTransformAnimation() &&
+         !style.SubtreeWillChangeContents();
 }
 
 bool CompositingReasonFinder::RequiresCompositingForRootScroller(
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
index c7c86da..0d9a581fe 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
@@ -149,50 +149,25 @@
   style->SetSubtreeWillChangeContents(false);
 
   style->SetHasCurrentTransformAnimation(false);
-  style->SetIsRunningTransformAnimationOnCompositor(false);
-  EXPECT_FALSE(
-      CompositingReasonFinder::RequiresCompositingForTransformAnimation(
-          *style));
-
-  style->SetHasCurrentTransformAnimation(false);
-  style->SetIsRunningTransformAnimationOnCompositor(true);
   EXPECT_FALSE(
       CompositingReasonFinder::RequiresCompositingForTransformAnimation(
           *style));
 
   style->SetHasCurrentTransformAnimation(true);
-  style->SetIsRunningTransformAnimationOnCompositor(false);
-  EXPECT_TRUE(CompositingReasonFinder::RequiresCompositingForTransformAnimation(
-      *style));
-
-  style->SetHasCurrentTransformAnimation(true);
-  style->SetIsRunningTransformAnimationOnCompositor(true);
   EXPECT_TRUE(CompositingReasonFinder::RequiresCompositingForTransformAnimation(
       *style));
 
   style->SetSubtreeWillChangeContents(true);
 
   style->SetHasCurrentTransformAnimation(false);
-  style->SetIsRunningTransformAnimationOnCompositor(false);
-  EXPECT_FALSE(
-      CompositingReasonFinder::RequiresCompositingForTransformAnimation(
-          *style));
-
-  style->SetHasCurrentTransformAnimation(false);
-  style->SetIsRunningTransformAnimationOnCompositor(true);
-  EXPECT_TRUE(CompositingReasonFinder::RequiresCompositingForTransformAnimation(
-      *style));
-
-  style->SetHasCurrentTransformAnimation(true);
-  style->SetIsRunningTransformAnimationOnCompositor(false);
   EXPECT_FALSE(
       CompositingReasonFinder::RequiresCompositingForTransformAnimation(
           *style));
 
   style->SetHasCurrentTransformAnimation(true);
-  style->SetIsRunningTransformAnimationOnCompositor(true);
-  EXPECT_TRUE(CompositingReasonFinder::RequiresCompositingForTransformAnimation(
-      *style));
+  EXPECT_FALSE(
+      CompositingReasonFinder::RequiresCompositingForTransformAnimation(
+          *style));
 }
 
 TEST_F(CompositingReasonFinderTest, CompositingReasonsForAnimation) {
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl.cc b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
index fa19568..60efeb5 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl.cc
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
@@ -106,7 +106,6 @@
   state.local_transform_space = &TransformPaintPropertyNode::Root();
   state.compositor_element_id = element_id_;
   state.direct_compositing_reasons = CompositingReason::kActiveOpacityAnimation;
-  state.is_running_opacity_animation_on_compositor = true;
   effect_ = EffectPaintPropertyNode::Create(EffectPaintPropertyNode::Root(),
                                             std::move(state));
 #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc b/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc
index 4270a73f..2cc79150cd 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc
@@ -334,7 +334,7 @@
   // node.
   EXPECT_EQ(highlight->Effect().GetCompositorElementId(),
             highlight->ElementIdForTesting());
-  EXPECT_TRUE(highlight->Effect().IsRunningOpacityAnimationOnCompositor());
+  EXPECT_TRUE(highlight->Effect().HasActiveOpacityAnimation());
 
   touch_node->remove(IGNORE_EXCEPTION_FOR_TESTING);
   UpdateAllLifecyclePhases();
@@ -381,7 +381,7 @@
   // node.
   const auto& effect = highlight->Effect();
   EXPECT_EQ(effect.GetCompositorElementId(), highlight->ElementIdForTesting());
-  EXPECT_TRUE(effect.IsRunningOpacityAnimationOnCompositor());
+  EXPECT_TRUE(effect.HasActiveOpacityAnimation());
 
   const auto& first_fragment = touch_node->GetLayoutObject()->FirstFragment();
   const auto* second_fragment = first_fragment.NextFragment();
diff --git a/third_party/blink/renderer/core/paint/object_paint_properties.h b/third_party/blink/renderer/core/paint/object_paint_properties.h
index cba5a47..c84ea9e 100644
--- a/third_party/blink/renderer/core/paint/object_paint_properties.h
+++ b/third_party/blink/renderer/core/paint/object_paint_properties.h
@@ -60,8 +60,10 @@
   const type##PaintPropertyNode* function() const { return variable.get(); } \
   PaintPropertyChangeType Update##function(                                  \
       const type##PaintPropertyNode& parent,                                 \
-      type##PaintPropertyNode::State&& state) {                              \
-    return Update(variable, parent, std::move(state));                       \
+      type##PaintPropertyNode::State&& state,                                \
+      const type##PaintPropertyNode::AnimationState& animation_state =       \
+          type##PaintPropertyNode::AnimationState()) {                       \
+    return Update(variable, parent, std::move(state), animation_state);      \
   }                                                                          \
   bool Clear##function() { return Clear(variable); }                         \
                                                                              \
@@ -251,11 +253,13 @@
   // created), and false otherwise. See the class-level comment ("update & clear
   // implementation note") for details about why this is needed for efficiency.
   template <typename PaintPropertyNode>
-  PaintPropertyChangeType Update(scoped_refptr<PaintPropertyNode>& field,
-                                 const PaintPropertyNode& parent,
-                                 typename PaintPropertyNode::State&& state) {
+  PaintPropertyChangeType Update(
+      scoped_refptr<PaintPropertyNode>& field,
+      const PaintPropertyNode& parent,
+      typename PaintPropertyNode::State&& state,
+      const typename PaintPropertyNode::AnimationState& animation_state) {
     if (field) {
-      auto changed = field->Update(parent, std::move(state));
+      auto changed = field->Update(parent, std::move(state), animation_state);
 #if DCHECK_IS_ON()
       DCHECK(!is_immutable_ || changed == PaintPropertyChangeType::kUnchanged)
           << "Value changed while immutable. New state:\n"
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 38113da..a74e7383 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -665,8 +665,11 @@
         // disable 2d translation optimization to ensure that the compositor
         // gets the correct origin (which might be omitted by the optimization)
         // to the compositor, in case later animated values will use the origin.
+        // See http://crbug.com/937929 for why we are not using
+        // style.IsRunningTransformAnimationOnCompositor() here.
         bool disable_2d_translation_optimization =
-            style.IsRunningTransformAnimationOnCompositor();
+            full_context_.direct_compositing_reasons &
+            CompositingReason::kActiveTransformAnimation;
         state.transform_and_origin =
             TransformPaintPropertyNode::TransformAndOrigin(
                 matrix, TransformOrigin(box),
@@ -701,12 +704,13 @@
         state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
             object_.UniqueId(),
             CompositorElementIdNamespace::kPrimaryTransform);
-        state.is_running_animation_on_compositor =
-            style.IsRunningTransformAnimationOnCompositor();
       }
 
+      TransformPaintPropertyNode::AnimationState animation_state;
+      animation_state.is_running_animation_on_compositor =
+          style.IsRunningTransformAnimationOnCompositor();
       OnUpdate(properties_->UpdateTransform(*context_.current.transform,
-                                            std::move(state)));
+                                            std::move(state), animation_state));
     } else {
       OnClear(properties_->ClearTransform());
     }
@@ -951,14 +955,15 @@
           state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
               object_.UniqueId(), CompositorElementIdNamespace::kPrimary);
         }
-        state.is_running_opacity_animation_on_compositor =
-            style.IsRunningOpacityAnimationOnCompositor();
-        state.is_running_backdrop_filter_animation_on_compositor =
-            style.IsRunningBackdropFilterAnimationOnCompositor();
       }
 
+      EffectPaintPropertyNode::AnimationState animation_state;
+      animation_state.is_running_opacity_animation_on_compositor =
+          style.IsRunningOpacityAnimationOnCompositor();
+      animation_state.is_running_backdrop_filter_animation_on_compositor =
+          style.IsRunningBackdropFilterAnimationOnCompositor();
       OnUpdate(properties_->UpdateEffect(*context_.current_effect,
-                                         std::move(state)));
+                                         std::move(state), animation_state));
 
       if (mask_clip || has_spv1_composited_clip_path) {
         EffectPaintPropertyNode::State mask_state;
@@ -1039,8 +1044,6 @@
 
 void FragmentPaintPropertyTreeBuilder::UpdateFilter() {
   DCHECK(properties_);
-  const ComputedStyle& style = object_.StyleRef();
-
   if (NeedsPaintPropertyUpdate()) {
     if (NeedsFilter(object_, full_context_.direct_compositing_reasons)) {
       EffectPaintPropertyNode::State state;
@@ -1096,17 +1099,15 @@
         state.direct_compositing_reasons =
             full_context_.direct_compositing_reasons &
             CompositingReason::kDirectReasonsForFilterProperty;
-        DCHECK(!style.HasCurrentFilterAnimation() ||
-               state.direct_compositing_reasons != CompositingReason::kNone);
-
         state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
             object_.UniqueId(), CompositorElementIdNamespace::kEffectFilter);
-        state.is_running_filter_animation_on_compositor =
-            style.IsRunningFilterAnimationOnCompositor();
       }
 
+      EffectPaintPropertyNode::AnimationState animation_state;
+      animation_state.is_running_filter_animation_on_compositor =
+          object_.StyleRef().IsRunningFilterAnimationOnCompositor();
       OnUpdate(properties_->UpdateFilter(*context_.current_effect,
-                                         std::move(state)));
+                                         std::move(state), animation_state));
     } else {
       OnClear(properties_->ClearFilter());
     }
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 03722e9..283b59f5 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
@@ -686,7 +686,8 @@
 
 TEST_P(PaintPropertyTreeBuilderTest,
        TransformNodeWithActiveAnimationHasDirectCompositingReason) {
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+      !RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
     return;
 
   LoadTestData("transform-animation.html");
@@ -707,7 +708,8 @@
 
 TEST_P(PaintPropertyTreeBuilderTest,
        EffectNodeWithActiveAnimationHasDirectCompositingReason) {
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+      !RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
     return;
 
   LoadTestData("opacity-animation.html");
@@ -4857,7 +4859,8 @@
 
 TEST_P(PaintPropertyTreeBuilderTest,
        TransformNodeNotAnimatedStillHasCompositorElementId) {
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+      !RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
     return;
 
   SetBodyInnerHTML("<div id='target' style='transform: translateX(2em)'></div");
@@ -4869,7 +4872,8 @@
 
 TEST_P(PaintPropertyTreeBuilderTest,
        EffectNodeNotAnimatedStillHasCompositorElementId) {
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+      !RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
     return;
 
   SetBodyInnerHTML("<div id='target' style='opacity: 0.5'></div");
@@ -4884,7 +4888,8 @@
 
 TEST_P(PaintPropertyTreeBuilderTest,
        TransformNodeAnimatedHasCompositorElementId) {
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+      !RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
     return;
 
   LoadTestData("transform-animation.html");
@@ -4896,7 +4901,8 @@
 }
 
 TEST_P(PaintPropertyTreeBuilderTest, EffectNodeAnimatedHasCompositorElementId) {
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+      !RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
     return;
 
   LoadTestData("opacity-animation.html");
@@ -4935,7 +4941,8 @@
 }
 
 TEST_P(PaintPropertyTreeBuilderTest, ScrollNodeHasCompositorElementId) {
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+      !RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
     return;
 
   SetBodyInnerHTML(R"HTML(
@@ -5645,7 +5652,8 @@
   )HTML");
 
   // When the root scrolls, there should be direct compositing reasons.
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
+      RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
     EXPECT_TRUE(DocScrollTranslation()->HasDirectCompositingReasons());
 
   // Remove scrolling from the root.
@@ -5673,7 +5681,8 @@
   )HTML");
   UpdateAllLifecyclePhasesForTest();
 
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
+      RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
     EXPECT_TRUE(DocScrollTranslation()->HasDirectCompositingReasons());
 
   // When the child iframe scrolls, there should not be direct compositing
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
index 78a548bb..57d2ce3 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
@@ -1636,7 +1636,7 @@
   const auto* transform_node =
       target->FirstFragment().PaintProperties()->Transform();
   ASSERT_TRUE(transform_node);
-  EXPECT_TRUE(transform_node->IsRunningAnimationOnCompositor());
+  EXPECT_TRUE(transform_node->HasActiveTransformAnimation());
   EXPECT_EQ(TransformationMatrix(), transform_node->Matrix());
   EXPECT_EQ(FloatPoint3D(50, 50, 0), transform_node->Origin());
   // Change of animation status should update PaintArtifactCompositor.
@@ -1661,7 +1661,7 @@
 
   ASSERT_EQ(transform_node,
             target->FirstFragment().PaintProperties()->Transform());
-  EXPECT_TRUE(transform_node->IsRunningAnimationOnCompositor());
+  EXPECT_TRUE(transform_node->HasActiveTransformAnimation());
   EXPECT_EQ(TransformationMatrix().Rotate(10), transform_node->Matrix());
   EXPECT_EQ(FloatPoint3D(50, 50, 0), transform_node->Origin());
   // Only transform value change during composited animation should not schedule
@@ -1680,7 +1680,7 @@
 
   ASSERT_EQ(transform_node,
             target->FirstFragment().PaintProperties()->Transform());
-  EXPECT_TRUE(transform_node->IsRunningAnimationOnCompositor());
+  EXPECT_TRUE(transform_node->HasActiveTransformAnimation());
   EXPECT_EQ(TransformationMatrix().Rotate(10), transform_node->Matrix());
   EXPECT_EQ(FloatPoint3D(70, 30, 0), transform_node->Origin());
   // Only transform value change during composited animation should not schedule
diff --git a/third_party/blink/renderer/core/paint/paint_timing.cc b/third_party/blink/renderer/core/paint/paint_timing.cc
index 8a70525..19b8a0a 100644
--- a/third_party/blink/renderer/core/paint/paint_timing.cc
+++ b/third_party/blink/renderer/core/paint/paint_timing.cc
@@ -235,6 +235,8 @@
 
 void PaintTiming::SetFirstContentfulPaintSwap(TimeTicks stamp) {
   DCHECK(first_contentful_paint_swap_.is_null());
+  TRACE_EVENT_INSTANT_WITH_TIMESTAMP0("loading", "FirstContentfulPaint",
+                                      TRACE_EVENT_SCOPE_GLOBAL, stamp);
   first_contentful_paint_swap_ = stamp;
   probe::PaintTiming(GetSupplementable(), "firstContentfulPaint",
                      first_contentful_paint_swap_.since_origin().InSecondsF());
diff --git a/third_party/blink/renderer/core/paint/svg_filter_painter.cc b/third_party/blink/renderer/core/paint/svg_filter_painter.cc
index 4937fa5..cfa1cca 100644
--- a/third_party/blink/renderer/core/paint/svg_filter_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_filter_painter.cc
@@ -20,7 +20,7 @@
 
 GraphicsContext* SVGFilterRecordingContext::BeginContent() {
   // Create a new context so the contents of the filter can be drawn and cached.
-  paint_controller_ = PaintController::Create();
+  paint_controller_ = std::make_unique<PaintController>();
   context_ = std::make_unique<GraphicsContext>(*paint_controller_);
 
   // Use initial_context_'s current paint chunk properties so that any new
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
index e42a70f..fface27 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
@@ -434,6 +434,51 @@
   EXPECT_EQ(CountVisibleTexts(), 0u);
 }
 
+TEST_F(TextPaintTimingDetectorTest, ClippedByParentVisibleRect) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #outer1 {
+        overflow: hidden;
+        height: 1px;
+        width: 1px;
+      }
+      #outer2 {
+        overflow: hidden;
+        height: 2px;
+        width: 2px;
+      }
+    </style>
+    <div id='outer1'></div>
+    <div id='outer2'></div>
+  )HTML");
+  Element* div1 = GetDocument().CreateRawElement(html_names::kDivTag);
+  Text* text1 = GetDocument().createTextNode(
+      "########################################################################"
+      "######################################################################"
+      "#");
+  div1->AppendChild(text1);
+  GetDocument().body()->getElementById("outer1")->AppendChild(div1);
+
+  UpdateAllLifecyclePhasesAndSimulateSwapTime();
+  EXPECT_EQ(TextRecordOfLargestTextPaint()->node_id, NodeIdOfText(div1));
+  EXPECT_EQ(TextRecordOfLargestTextPaint()->first_size, 1u);
+
+  Element* div2 = GetDocument().CreateRawElement(html_names::kDivTag);
+  Text* text2 = GetDocument().createTextNode(
+      "########################################################################"
+      "######################################################################"
+      "#");
+  div2->AppendChild(text2);
+  GetDocument().body()->getElementById("outer2")->AppendChild(div2);
+
+  UpdateAllLifecyclePhasesAndSimulateSwapTime();
+  EXPECT_EQ(TextRecordOfLargestTextPaint()->node_id, NodeIdOfText(div2));
+  // This size is larger than the size of the first object . But the exact size
+  // depends on different platforms. We only need to ensure this size is larger
+  // than the first size.
+  EXPECT_GT(TextRecordOfLargestTextPaint()->first_size, 1u);
+}
+
 TEST_F(TextPaintTimingDetectorTest, Iframe) {
   SetBodyInnerHTML(R"HTML(
     <iframe width=100px height=100px></iframe>
diff --git a/third_party/blink/renderer/core/paint/text_painter_test.cc b/third_party/blink/renderer/core/paint/text_painter_test.cc
index 99a269a..af513cbe 100644
--- a/third_party/blink/renderer/core/paint/text_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/text_painter_test.cc
@@ -24,7 +24,7 @@
  public:
   TextPainterTest()
       : layout_text_(nullptr),
-        paint_controller_(PaintController::Create()),
+        paint_controller_(std::make_unique<PaintController>()),
         context_(*paint_controller_) {}
 
  protected:
diff --git a/third_party/blink/renderer/core/streams/miscellaneous_operations.cc b/third_party/blink/renderer/core/streams/miscellaneous_operations.cc
index 5e7e349..d56a1a83 100644
--- a/third_party/blink/renderer/core/streams/miscellaneous_operations.cc
+++ b/third_party/blink/renderer/core/streams/miscellaneous_operations.cc
@@ -10,6 +10,9 @@
 #include <math.h>
 
 #include "base/optional.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_readable_stream.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_writable_stream.h"
+#include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/core/streams/stream_algorithms.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
@@ -452,4 +455,226 @@
   return PromiseRejectInternal(script_state, value, 0);
 }
 
+CORE_EXPORT void GetReaderValidateOptions(ScriptState* script_state,
+                                          ScriptValue options,
+                                          ExceptionState& exception_state) {
+  // https://streams.spec.whatwg.org/#rs-get-reader
+  // The unpacking of |options| is indicated as part of the signature of the
+  // function in the standard.
+  v8::TryCatch block(script_state->GetIsolate());
+  v8::Local<v8::Value> mode;
+  v8::Local<v8::String> mode_string;
+  v8::Local<v8::Context> context = script_state->GetContext();
+  if (options.V8Value()->IsUndefined()) {
+    mode = v8::Undefined(script_state->GetIsolate());
+  } else {
+    v8::Local<v8::Object> v8_options;
+    if (!options.V8Value()->ToObject(context).ToLocal(&v8_options)) {
+      exception_state.RethrowV8Exception(block.Exception());
+      return;
+    }
+    if (!v8_options->Get(context, V8String(script_state->GetIsolate(), "mode"))
+             .ToLocal(&mode)) {
+      exception_state.RethrowV8Exception(block.Exception());
+      return;
+    }
+  }
+
+  // 3. Set mode to ? ToString(mode).
+  if (!mode->ToString(context).ToLocal(&mode_string)) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return;
+  }
+
+  // 4. If mode is "byob", return ? AcquireReadableStreamBYOBReader(this, true).
+  if (ToCoreString(mode_string) == "byob") {
+    // TODO(ricea): Support BYOB readers.
+    exception_state.ThrowTypeError("invalid mode");
+    return;
+  }
+
+  if (!mode->IsUndefined()) {
+    // 5. Throw a RangeError exception.
+    exception_state.ThrowRangeError("invalid mode");
+    return;
+  }
+}
+
+CORE_EXPORT void PipeThroughExtractReadableWritable(
+    ScriptState* script_state,
+    const ReadableStream* stream,
+    ScriptValue transform_stream,
+    ScriptValue* readable_stream,
+    WritableStream** writable_stream,
+    ExceptionState& exception_state) {
+  DCHECK(readable_stream);
+  DCHECK(writable_stream);
+  // https://streams.spec.whatwg.org/#rs-pipe-through
+  // The first part of this function implements the unpacking of the {readable,
+  // writable} argument to the method.
+  v8::Local<v8::Value> pair_value = transform_stream.V8Value();
+  v8::Local<v8::Context> context = script_state->GetContext();
+
+  constexpr char kWritableIsNotWritableStream[] =
+      "parameter 1's 'writable' property is not a WritableStream.";
+  constexpr char kReadableIsNotReadableStream[] =
+      "parameter 1's 'readable' property is not a ReadableStream.";
+  constexpr char kWritableIsLocked[] = "parameter 1's 'writable' is locked.";
+
+  v8::Local<v8::Object> pair;
+  if (!pair_value->ToObject(context).ToLocal(&pair)) {
+    exception_state.ThrowTypeError(kWritableIsNotWritableStream);
+    return;
+  }
+
+  v8::Isolate* isolate = script_state->GetIsolate();
+  v8::Local<v8::Value> writable, readable;
+  {
+    v8::TryCatch block(isolate);
+    if (!pair->Get(context, V8String(isolate, "writable")).ToLocal(&writable)) {
+      exception_state.RethrowV8Exception(block.Exception());
+      return;
+    }
+    DCHECK(!block.HasCaught());
+
+    if (!pair->Get(context, V8String(isolate, "readable")).ToLocal(&readable)) {
+      exception_state.RethrowV8Exception(block.Exception());
+      return;
+    }
+    DCHECK(!block.HasCaught());
+  }
+
+  // 2. If ! IsWritableStream(_writable_) is *false*, throw a *TypeError*
+  //    exception.
+  WritableStream* dom_writable =
+      V8WritableStream::ToImplWithTypeCheck(isolate, writable);
+  if (!dom_writable) {
+    exception_state.ThrowTypeError(kWritableIsNotWritableStream);
+    return;
+  }
+
+  // 3. If ! IsReadableStream(_readable_) is *false*, throw a *TypeError*
+  //    exception.
+  if (!V8ReadableStream::HasInstance(readable, isolate)) {
+    exception_state.ThrowTypeError(kReadableIsNotReadableStream);
+    return;
+  }
+
+  // TODO(ricea): When aborting pipes is supported, implement step 5:
+  // 5. If _signal_ is not *undefined*, and _signal_ is not an instance of the
+  //    `AbortSignal` interface, throw a *TypeError* exception.
+
+  // 6. If ! IsReadableStreamLocked(*this*) is *true*, throw a *TypeError*
+  //    exception.
+  if (stream->IsLocked(script_state, exception_state).value_or(false)) {
+    exception_state.ThrowTypeError("Cannot pipe a locked stream");
+    return;
+  }
+  if (exception_state.HadException()) {
+    return;
+  }
+
+  // 7. If ! IsWritableStreamLocked(_writable_) is *true*, throw a *TypeError*
+  //    exception.
+  if (dom_writable->IsLocked(script_state, exception_state).value_or(false)) {
+    exception_state.ThrowTypeError(kWritableIsLocked);
+    return;
+  }
+  if (exception_state.HadException()) {
+    return;
+  }
+  *writable_stream = dom_writable;
+  *readable_stream = ScriptValue(script_state, readable);
+}
+
+CORE_EXPORT WritableStream* PipeToCheckSourceAndDestination(
+    ScriptState* script_state,
+    ReadableStream* source,
+    ScriptValue destination_value,
+    ExceptionState& exception_state) {
+  // https://streams.spec.whatwg.org/#rs-pipe-to
+
+  // 2. If ! IsWritableStream(dest) is false, return a promise rejected with a
+  // TypeError exception.
+  WritableStream* destination = V8WritableStream::ToImplWithTypeCheck(
+      script_state->GetIsolate(), destination_value.V8Value());
+
+  if (!destination) {
+    exception_state.ThrowTypeError("Illegal invocation");
+    return nullptr;
+  }
+
+  // Step 3. is done separately afterwards.
+
+  // TODO(ricea): When aborting pipes is supported, implement step 4:
+  // 4. If signal is not undefined, and signal is not an instance of the
+  // AbortSignal interface, return a promise rejected with a TypeError
+  // exception.
+
+  // 5. If ! IsReadableStreamLocked(this) is true, return a promise rejected
+  // with a TypeError exception.
+  if (source->locked(script_state, exception_state) &&
+      !exception_state.HadException()) {
+    exception_state.ThrowTypeError("Cannot pipe a locked stream");
+    return nullptr;
+  }
+  if (exception_state.HadException())
+    return nullptr;
+
+  // 6. If ! IsWritableStreamLocked(dest) is true, return a promise rejected
+  // with a TypeError exception.
+  if (destination->locked(script_state, exception_state) &&
+      !exception_state.HadException()) {
+    exception_state.ThrowTypeError("Cannot pipe to a locked stream");
+    return nullptr;
+  }
+  if (exception_state.HadException())
+    return nullptr;
+
+  return destination;
+}
+
+ScriptValue CallTeeAndReturnBranchArray(ScriptState* script_state,
+                                        ReadableStream* readable,
+                                        ExceptionState& exception_state) {
+  // https://streams.spec.whatwg.org/#rs-tee
+  v8::Isolate* isolate = script_state->GetIsolate();
+  ReadableStream* branch1 = nullptr;
+  ReadableStream* branch2 = nullptr;
+
+  // 2. Let branches be ? ReadableStreamTee(this, false).
+  readable->Tee(script_state, &branch1, &branch2, exception_state);
+
+  if (!branch1 || !branch2)
+    return ScriptValue();
+
+  DCHECK(!exception_state.HadException());
+
+  // 3. Return ! CreateArrayFromList(branches).
+  v8::TryCatch block(isolate);
+  v8::Local<v8::Context> context = script_state->GetContext();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, 2);
+  v8::Local<v8::Object> global = context->Global();
+
+  v8::Local<v8::Value> v8_branch1 = ToV8(branch1, global, isolate);
+  if (v8_branch1.IsEmpty()) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return ScriptValue();
+  }
+  v8::Local<v8::Value> v8_branch2 = ToV8(branch2, global, isolate);
+  if (v8_branch1.IsEmpty()) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return ScriptValue();
+  }
+  if (array->Set(context, V8String(isolate, "0"), v8_branch1).IsNothing()) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return ScriptValue();
+  }
+  if (array->Set(context, V8String(isolate, "1"), v8_branch2).IsNothing()) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return ScriptValue();
+  }
+  return ScriptValue(script_state, array);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/streams/miscellaneous_operations.h b/third_party/blink/renderer/core/streams/miscellaneous_operations.h
index 3e0c42d..d396a17 100644
--- a/third_party/blink/renderer/core/streams/miscellaneous_operations.h
+++ b/third_party/blink/renderer/core/streams/miscellaneous_operations.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_MISCELLANEOUS_OPERATIONS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_MISCELLANEOUS_OPERATIONS_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "v8/include/v8.h"
 
@@ -14,10 +15,12 @@
 // https://streams.spec.whatwg.org/#misc-abstract-ops
 
 class ExceptionState;
+class ReadableStream;
 class ScriptState;
 class StrategySizeAlgorithm;
 class StreamAlgorithm;
 class StreamStartAlgorithm;
+class WritableStream;
 
 // This is slightly different than the version in the standard
 // https://streams.spec.whatwg.org/#create-algorithm-from-underlying-method as
@@ -91,6 +94,48 @@
 // Implements "a promise resolved with *undefined*".
 CORE_EXPORT v8::Local<v8::Promise> PromiseResolveWithUndefined(ScriptState*);
 
+// Validates the "options" argument to ReadableStream::getReader(). This
+// implementation is shared between ReadableStreamWrapper and
+// ReadableStreamNative. If an exception is thrown validation failed.
+// TODO(ricea): Move it into the native implementation once
+// ReadableStreamWrapper is deleted.
+CORE_EXPORT void GetReaderValidateOptions(ScriptState*,
+                                          ScriptValue options,
+                                          ExceptionState&);
+
+// Extracts the "readable" and "writable" streams from the |transform_stream|
+// dictionary, validates them, and returns them via the |readable_stream| and
+// |writable_stream| out parameters. The types of |readable_stream| and
+// |writable_stream| are asymmetric because |readable_stream| is returned
+// directly to JavaScript and so there is no point in converting it to an
+// internal type.
+// TODO(ricea): Move it into the native implementation once
+// ReadableStreamWrapper is deleted.
+CORE_EXPORT void PipeThroughExtractReadableWritable(
+    ScriptState*,
+    const ReadableStream* stream,
+    ScriptValue transform_stream,
+    ScriptValue* readable_stream,
+    WritableStream** writable_stream,
+    ExceptionState&);
+
+// Verifies that |destination_value| is a WritableStream and that both it and
+// |source| are unlocked. Returns the WritableStream that was wrapped by
+// |destination_value|.
+CORE_EXPORT WritableStream* PipeToCheckSourceAndDestination(
+    ScriptState*,
+    ReadableStream* source,
+    ScriptValue destination_value,
+    ExceptionState&);
+
+// Calls Tee() on |readable|, converts the two branches to a JavaScript array
+// and returns them.
+// TODO(ricea): Move it into the native implementation once
+// ReadableStreamWrapper is deleted.
+ScriptValue CallTeeAndReturnBranchArray(ScriptState* script_state,
+                                        ReadableStream* readable,
+                                        ExceptionState& exception_state);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_MISCELLANEOUS_OPERATIONS_H_
diff --git a/third_party/blink/renderer/core/streams/readable_stream_wrapper.cc b/third_party/blink/renderer/core/streams/readable_stream_wrapper.cc
index e855410..da9ca1d 100644
--- a/third_party/blink/renderer/core/streams/readable_stream_wrapper.cc
+++ b/third_party/blink/renderer/core/streams/readable_stream_wrapper.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_readable_stream.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_writable_stream.h"
+#include "third_party/blink/renderer/core/streams/miscellaneous_operations.h"
 #include "third_party/blink/renderer/core/streams/readable_stream_operations.h"
 #include "third_party/blink/renderer/core/streams/retain_wrapper_during_construction.h"
 #include "third_party/blink/renderer/core/streams/writable_stream_wrapper.h"
@@ -195,35 +196,8 @@
 ScriptValue ReadableStreamWrapper::getReader(ScriptState* script_state,
                                              ScriptValue options,
                                              ExceptionState& exception_state) {
-  v8::TryCatch block(script_state->GetIsolate());
-  v8::Local<v8::Value> mode;
-  v8::Local<v8::String> mode_string;
-  v8::Local<v8::Context> context = script_state->GetContext();
-  if (options.V8Value()->IsUndefined()) {
-    mode = v8::Undefined(script_state->GetIsolate());
-  } else {
-    v8::Local<v8::Object> v8_options;
-    if (!options.V8Value()->ToObject(context).ToLocal(&v8_options)) {
-      exception_state.RethrowV8Exception(block.Exception());
-      return ScriptValue();
-    }
-    if (!v8_options->Get(context, V8String(script_state->GetIsolate(), "mode"))
-             .ToLocal(&mode)) {
-      exception_state.RethrowV8Exception(block.Exception());
-      return ScriptValue();
-    }
-  }
-
-  if (!mode->ToString(context).ToLocal(&mode_string)) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return ScriptValue();
-  }
-  if (ToCoreString(mode_string) == "byob") {
-    exception_state.ThrowTypeError("invalid mode");
-    return ScriptValue();
-  }
-  if (!mode->IsUndefined()) {
-    exception_state.ThrowRangeError("invalid mode");
+  GetReaderValidateOptions(script_state, options, exception_state);
+  if (exception_state.HadException()) {
     return ScriptValue();
   }
   return ReadableStreamOperations::GetReader(
@@ -246,74 +220,10 @@
     ScriptValue transform_stream,
     ScriptValue options,
     ExceptionState& exception_state) {
-  v8::Local<v8::Value> pair_value = transform_stream.V8Value();
-  v8::Local<v8::Context> context = script_state->GetContext();
-
-  constexpr char kWritableIsNotWritableStream[] =
-      "parameter 1's 'writable' property is not a WritableStream.";
-  constexpr char kReadableIsNotReadableStream[] =
-      "parameter 1's 'readable' property is not a ReadableStream.";
-  constexpr char kWritableIsLocked[] = "parameter 1's 'writable' is locked.";
-
-  v8::Local<v8::Object> pair;
-  if (!pair_value->ToObject(context).ToLocal(&pair)) {
-    exception_state.ThrowTypeError(kWritableIsNotWritableStream);
-    return ScriptValue();
-  }
-
-  v8::Isolate* isolate = script_state->GetIsolate();
-  v8::Local<v8::Value> writable, readable;
-  {
-    v8::TryCatch block(isolate);
-    if (!pair->Get(context, V8String(isolate, "writable")).ToLocal(&writable)) {
-      exception_state.RethrowV8Exception(block.Exception());
-      return ScriptValue();
-    }
-    DCHECK(!block.HasCaught());
-
-    if (!pair->Get(context, V8String(isolate, "readable")).ToLocal(&readable)) {
-      exception_state.RethrowV8Exception(block.Exception());
-      return ScriptValue();
-    }
-    DCHECK(!block.HasCaught());
-  }
-
-  // 2. If ! IsWritableStream(_writable_) is *false*, throw a *TypeError*
-  //    exception.
-  WritableStream* dom_writable =
-      V8WritableStream::ToImplWithTypeCheck(isolate, writable);
-  if (!dom_writable) {
-    exception_state.ThrowTypeError(kWritableIsNotWritableStream);
-    return ScriptValue();
-  }
-
-  // 3. If ! IsReadableStream(_readable_) is *false*, throw a *TypeError*
-  //    exception.
-  if (!V8ReadableStream::HasInstance(readable, isolate)) {
-    exception_state.ThrowTypeError(kReadableIsNotReadableStream);
-    return ScriptValue();
-  }
-
-  // TODO(ricea): When aborting pipes is supported, implement step 5:
-  // 5. If _signal_ is not *undefined*, and _signal_ is not an instance of the
-  //    `AbortSignal` interface, throw a *TypeError* exception.
-
-  // 6. If ! IsReadableStreamLocked(*this*) is *true*, throw a *TypeError*
-  //    exception.
-  if (IsLocked(script_state, exception_state).value_or(false)) {
-    exception_state.ThrowTypeError("Cannot pipe a locked stream");
-    return ScriptValue();
-  }
-  if (exception_state.HadException()) {
-    return ScriptValue();
-  }
-
-  // 7. If ! IsWritableStreamLocked(_writable_) is *true*, throw a *TypeError*
-  //    exception.
-  if (dom_writable->IsLocked(script_state, exception_state).value_or(false)) {
-    exception_state.ThrowTypeError(kWritableIsLocked);
-    return ScriptValue();
-  }
+  ScriptValue readable;
+  WritableStream* writable = nullptr;
+  PipeThroughExtractReadableWritable(script_state, this, transform_stream,
+                                     &readable, &writable, exception_state);
   if (exception_state.HadException()) {
     return ScriptValue();
   }
@@ -329,15 +239,12 @@
   // This cast is safe because the following code will only be run when the
   // native version of WritableStream is not in use.
   WritableStreamWrapper* writable_wrapper =
-      static_cast<WritableStreamWrapper*>(dom_writable);
+      static_cast<WritableStreamWrapper*>(writable);
 
   // 8. Let _promise_ be ! ReadableStreamPipeTo(*this*, _writable_,
   //    _preventClose_, _preventAbort_, _preventCancel_,
   //   _signal_).
 
-  // TODO(ricea): Maybe change the parameters to
-  // ReadableStreamOperations::PipeTo to match ReadableStreamPipeTo() in the
-  // standard?
   ScriptPromise promise = ReadableStreamOperations::PipeTo(
       script_state, GetInternalStream(script_state),
       writable_wrapper->GetInternalStream(script_state), options,
@@ -350,7 +257,7 @@
   promise.MarkAsHandled();
 
   // 10. Return _readable_.
-  return ScriptValue(script_state, readable);
+  return readable;
 }
 
 ScriptPromise ReadableStreamWrapper::pipeTo(ScriptState* script_state,
@@ -366,27 +273,12 @@
                                             ScriptValue destination_value,
                                             ScriptValue options,
                                             ExceptionState& exception_state) {
-  WritableStream* destination = V8WritableStream::ToImplWithTypeCheck(
-      script_state->GetIsolate(), destination_value.V8Value());
-
-  if (!destination) {
-    exception_state.ThrowTypeError("Illegal invocation");
+  WritableStream* destination = PipeToCheckSourceAndDestination(
+      script_state, this, destination_value, exception_state);
+  if (exception_state.HadException()) {
     return ScriptPromise();
   }
-  if (locked(script_state, exception_state) &&
-      !exception_state.HadException()) {
-    exception_state.ThrowTypeError("Cannot pipe a locked stream");
-    return ScriptPromise();
-  }
-  if (exception_state.HadException())
-    return ScriptPromise();
-  if (destination->locked(script_state, exception_state) &&
-      !exception_state.HadException()) {
-    exception_state.ThrowTypeError("Cannot pipe to a locked stream");
-    return ScriptPromise();
-  }
-  if (exception_state.HadException())
-    return ScriptPromise();
+  DCHECK(destination);
 
   if (RuntimeEnabledFeatures::StreamsNativeEnabled()) {
     // TODO(ricea): Replace this with a DCHECK once ReadableStreamNative is
@@ -409,41 +301,7 @@
 
 ScriptValue ReadableStreamWrapper::tee(ScriptState* script_state,
                                        ExceptionState& exception_state) {
-  v8::Isolate* isolate = script_state->GetIsolate();
-  ReadableStream* branch1 = nullptr;
-  ReadableStream* branch2 = nullptr;
-
-  Tee(script_state, &branch1, &branch2, exception_state);
-
-  if (!branch1 || !branch2)
-    return ScriptValue();
-
-  DCHECK(!exception_state.HadException());
-
-  v8::TryCatch block(isolate);
-  v8::Local<v8::Context> context = script_state->GetContext();
-  v8::Local<v8::Array> array = v8::Array::New(isolate, 2);
-  v8::Local<v8::Object> global = context->Global();
-
-  v8::Local<v8::Value> v8_branch1 = ToV8(branch1, global, isolate);
-  if (v8_branch1.IsEmpty()) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return ScriptValue();
-  }
-  v8::Local<v8::Value> v8_branch2 = ToV8(branch2, global, isolate);
-  if (v8_branch1.IsEmpty()) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return ScriptValue();
-  }
-  if (array->Set(context, V8String(isolate, "0"), v8_branch1).IsNothing()) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return ScriptValue();
-  }
-  if (array->Set(context, V8String(isolate, "1"), v8_branch2).IsNothing()) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return ScriptValue();
-  }
-  return ScriptValue(script_state, array);
+  return CallTeeAndReturnBranchArray(script_state, this, exception_state);
 }
 
 void ReadableStreamWrapper::Tee(ScriptState* script_state,
diff --git a/third_party/blink/renderer/core/streams/stream_script_function.cc b/third_party/blink/renderer/core/streams/stream_script_function.cc
index 2bd72878..8204397 100644
--- a/third_party/blink/renderer/core/streams/stream_script_function.cc
+++ b/third_party/blink/renderer/core/streams/stream_script_function.cc
@@ -17,19 +17,32 @@
   CallWithLocal(args[0]);
 }
 
-void StreamThenPromise(v8::Local<v8::Context> context,
-                       v8::Local<v8::Promise> promise,
-                       StreamScriptFunction* on_fulfilled,
-                       StreamScriptFunction* on_rejected) {
-  DCHECK(on_fulfilled);
-  DCHECK(on_rejected);
-  auto result = promise->Then(context, on_fulfilled->BindToV8Function(),
-                              on_rejected->BindToV8Function());
-  if (result.IsEmpty()) {
+v8::Local<v8::Promise> StreamThenPromise(v8::Local<v8::Context> context,
+                                         v8::Local<v8::Promise> promise,
+                                         StreamScriptFunction* on_fulfilled,
+                                         StreamScriptFunction* on_rejected) {
+  v8::MaybeLocal<v8::Promise> result_maybe;
+  if (!on_fulfilled) {
+    DCHECK(on_rejected);
+    result_maybe = promise->Catch(context, on_rejected->BindToV8Function());
+  } else if (on_rejected) {
+    result_maybe = promise->Then(context, on_fulfilled->BindToV8Function(),
+                                 on_rejected->BindToV8Function());
+  } else {
+    result_maybe = promise->Then(context, on_fulfilled->BindToV8Function());
+  }
+
+  v8::Local<v8::Promise> result;
+  if (!result_maybe.ToLocal(&result)) {
     DVLOG(3)
         << "assuming that failure of promise->Then() is caused by shutdown and"
            "ignoring it";
+    // Try to create a dummy promise so that the calling code can continue. If
+    // we can't create one, then we can't return to the calling context so we
+    // have to crash. This shouldn't happen except on OOM.
+    result = v8::Promise::Resolver::New(context).ToLocalChecked()->GetPromise();
   }
+  return result;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/streams/stream_script_function.h b/third_party/blink/renderer/core/streams/stream_script_function.h
index 9ed75c0..599e26f 100644
--- a/third_party/blink/renderer/core/streams/stream_script_function.h
+++ b/third_party/blink/renderer/core/streams/stream_script_function.h
@@ -29,11 +29,13 @@
 
 // A convenient wrapper for promise->Then() for when both paths are
 // StreamScriptFunctions. It avoids having to call BindToV8Function()
-// explicitly. Both |on_fulfilled| and |on_rejected| must be non-null.
-void StreamThenPromise(v8::Local<v8::Context>,
-                       v8::Local<v8::Promise>,
-                       StreamScriptFunction* on_fulfilled,
-                       StreamScriptFunction* on_rejected);
+// explicitly. If |on_rejected| is null then behaves like single-argument
+// Then(). If |on_fulfilled| is null then it calls Catch().
+v8::Local<v8::Promise> StreamThenPromise(
+    v8::Local<v8::Context>,
+    v8::Local<v8::Promise>,
+    StreamScriptFunction* on_fulfilled,
+    StreamScriptFunction* on_rejected = nullptr);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/streams/writable_stream_default_writer.cc b/third_party/blink/renderer/core/streams/writable_stream_default_writer.cc
index 8e375dd5..7129ea11 100644
--- a/third_party/blink/renderer/core/streams/writable_stream_default_writer.cc
+++ b/third_party/blink/renderer/core/streams/writable_stream_default_writer.cc
@@ -318,6 +318,33 @@
   writer->ready_promise_->MarkAsHandled(isolate);
 }
 
+base::Optional<double> WritableStreamDefaultWriter::GetDesiredSizeInternal()
+    const {
+  // https://streams.spec.whatwg.org/#writable-stream-default-writer-get-desired-size
+  //  1. Let stream be writer.[[ownerWritableStream]].
+  const WritableStreamNative* stream = owner_writable_stream_;
+
+  //  2. Let state be stream.[[state]].
+  const auto state = stream->GetState();
+
+  switch (state) {
+    //  3. If state is "errored" or "erroring", return null.
+    case WritableStreamNative::kErrored:
+    case WritableStreamNative::kErroring:
+      return base::nullopt;
+
+      //  4. If state is "closed", return 0.
+    case WritableStreamNative::kClosed:
+      return 0.0;
+
+    default:
+      //  5. Return ! WritableStreamDefaultControllerGetDesiredSize(
+      //     stream.[[writableStreamController]]).
+      return WritableStreamDefaultController::GetDesiredSize(
+          stream->Controller());
+  }
+}
+
 void WritableStreamDefaultWriter::SetReadyPromise(
     StreamPromiseResolver* ready_promise) {
   ready_promise_ = ready_promise;
@@ -457,27 +484,17 @@
     const WritableStreamDefaultWriter* writer) {
   // https://streams.spec.whatwg.org/#writable-stream-default-writer-get-desired-size
   //  1. Let stream be writer.[[ownerWritableStream]].
-  const WritableStreamNative* stream = writer->owner_writable_stream_;
-
   //  2. Let state be stream.[[state]].
-  const auto state = stream->GetState();
-
   //  3. If state is "errored" or "erroring", return null.
-  if (state == WritableStreamNative::kErrored ||
-      state == WritableStreamNative::kErroring) {
+  base::Optional<double> desired_size = writer->GetDesiredSizeInternal();
+  if (!desired_size.has_value()) {
     return v8::Null(isolate);
   }
 
   //  4. If state is "closed", return 0.
-  if (state == WritableStreamNative::kClosed) {
-    return v8::Number::New(isolate, 0);
-  }
-
   //  5. Return ! WritableStreamDefaultControllerGetDesiredSize(
   //     stream.[[writableStreamController]]).
-  double desired_size =
-      WritableStreamDefaultController::GetDesiredSize(stream->Controller());
-  return v8::Number::New(isolate, desired_size);
+  return v8::Number::New(isolate, desired_size.value());
 }
 
 void WritableStreamDefaultWriter::Release(ScriptState* script_state,
diff --git a/third_party/blink/renderer/core/streams/writable_stream_default_writer.h b/third_party/blink/renderer/core/streams/writable_stream_default_writer.h
index e5805ff..6d3d672e 100644
--- a/third_party/blink/renderer/core/streams/writable_stream_default_writer.h
+++ b/third_party/blink/renderer/core/streams/writable_stream_default_writer.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_WRITABLE_STREAM_DEFAULT_WRITER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_WRITABLE_STREAM_DEFAULT_WRITER_H_
 
+#include "base/optional.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
 #include "v8/include/v8.h"
@@ -79,6 +80,12 @@
 
   StreamPromiseResolver* ClosedPromise() { return closed_promise_; }
   StreamPromiseResolver* ReadyPromise() { return ready_promise_; }
+  WritableStreamNative* OwnerWritableStream() { return owner_writable_stream_; }
+
+  // This is a variant of GetDesiredSize() that doesn't create an intermediate
+  // JavaScript object. Instead it returns base::nullopt where the JavaScript
+  // version would return null.
+  base::Optional<double> GetDesiredSizeInternal() const;
 
   void SetReadyPromise(StreamPromiseResolver*);
 
@@ -104,7 +111,6 @@
                                           WritableStreamDefaultWriter*,
                                           v8::Local<v8::Value> error);
 
-
   // https://streams.spec.whatwg.org/#writable-stream-default-writer-get-desired-size
   static v8::Local<v8::Value> GetDesiredSize(
       v8::Isolate* isolate,
diff --git a/third_party/blink/renderer/core/streams/writable_stream_native.cc b/third_party/blink/renderer/core/streams/writable_stream_native.cc
index c99e225..6464afa 100644
--- a/third_party/blink/renderer/core/streams/writable_stream_native.cc
+++ b/third_party/blink/renderer/core/streams/writable_stream_native.cc
@@ -122,6 +122,7 @@
   // 5. If type is not undefined, throw a RangeError exception.
   if (!type->IsUndefined()) {
     exception_state.ThrowRangeError("Invalid type is specified");
+    return;
   }
 
   // 6. Let sizeAlgorithm be ? MakeSizeAlgorithmFromSizeFunction(size).
diff --git a/third_party/blink/renderer/core/streams/writable_stream_native.h b/third_party/blink/renderer/core/streams/writable_stream_native.h
index fd13c23..1a2f12b 100644
--- a/third_party/blink/renderer/core/streams/writable_stream_native.h
+++ b/third_party/blink/renderer/core/streams/writable_stream_native.h
@@ -150,6 +150,8 @@
 
   // Accessors for use by other stream classes.
   State GetState() const { return state_; }
+  bool IsErrored() const { return state_ == kErrored; }
+  bool IsWritable() const { return state_ == kWritable; }
 
   bool HasBackpressure() const { return has_backpressure_; }
 
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index f20f260f..9dd4db74 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -883,14 +883,6 @@
       HasCurrentFilterAnimation() != other.HasCurrentFilterAnimation() ||
       HasCurrentBackdropFilterAnimation() !=
           other.HasCurrentBackdropFilterAnimation() ||
-      IsRunningTransformAnimationOnCompositor() !=
-          other.IsRunningTransformAnimationOnCompositor() ||
-      IsRunningOpacityAnimationOnCompositor() !=
-          other.IsRunningOpacityAnimationOnCompositor() ||
-      IsRunningFilterAnimationOnCompositor() !=
-          other.IsRunningFilterAnimationOnCompositor() ||
-      IsRunningBackdropFilterAnimationOnCompositor() !=
-          other.IsRunningBackdropFilterAnimationOnCompositor() ||
       SubtreeWillChangeContents() != other.SubtreeWillChangeContents() ||
       BackfaceVisibility() != other.BackfaceVisibility() ||
       HasWillChangeCompositingHint() != other.HasWillChangeCompositingHint() ||
diff --git a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
index 2b20a80..23a5a94 100644
--- a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
+++ b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
@@ -783,6 +783,9 @@
       field_template: "primitive",
       type_name: "bool",
       field_group: "*",
+      // This field just affects how changes of animatable values are handled,
+      // so it doesn't contribute to style differences.
+      custom_compare: true,
       default_value: "false",
     },
     {
@@ -790,6 +793,9 @@
       field_template: "primitive",
       type_name: "bool",
       field_group: "*",
+      // This field just affects how changes of animatable values are handled,
+      // so it doesn't contribute to style differences.
+      custom_compare: true,
       default_value: "false",
     },
     {
@@ -797,6 +803,9 @@
       field_template: "primitive",
       type_name: "bool",
       field_group: "*",
+      // This field just affects how changes of animatable values are handled,
+      // so it doesn't contribute to style differences.
+      custom_compare: true,
       default_value: "false",
     },
     {
@@ -804,6 +813,9 @@
       field_template: "primitive",
       type_name: "bool",
       field_group: "*",
+      // This field just affects how changes of animatable values are handled,
+      // so it doesn't contribute to style differences.
+      custom_compare: true,
       default_value: "false",
     },
     // A stacking context is painted atomically and defines a stacking order,
diff --git a/third_party/blink/renderer/core/style/computed_style_test.cc b/third_party/blink/renderer/core/style/computed_style_test.cc
index b05fbee5..4041261 100644
--- a/third_party/blink/renderer/core/style/computed_style_test.cc
+++ b/third_party/blink/renderer/core/style/computed_style_test.cc
@@ -417,18 +417,32 @@
     EXPECT_TRUE(diff.CompositingReasonsChanged());                         \
   } while (false)
 
+#define TEST_ANIMATION_FLAG_NO_DIFF(flag)                                  \
+  do {                                                                     \
+    auto style = ComputedStyle::Create();                                  \
+    auto other = ComputedStyle::Create();                                  \
+    EXPECT_FALSE(style->flag());                                           \
+    EXPECT_FALSE(other->flag());                                           \
+    style->Set##flag(true);                                                \
+    EXPECT_TRUE(style->flag());                                            \
+    EXPECT_EQ(ComputedStyle::Difference::kEqual,                           \
+              ComputedStyle::ComputeDifference(style.get(), other.get())); \
+    auto diff = style->VisualInvalidationDiff(*document, *other);          \
+    EXPECT_FALSE(diff.HasDifference());                                    \
+    EXPECT_FALSE(diff.CompositingReasonsChanged());                        \
+  } while (false)
+
 TEST(ComputedStyleTest, AnimationFlags) {
   Persistent<Document> document = Document::CreateForTest();
   TEST_ANIMATION_FLAG(HasCurrentTransformAnimation, kNonInherited);
   TEST_ANIMATION_FLAG(HasCurrentOpacityAnimation, kNonInherited);
   TEST_ANIMATION_FLAG(HasCurrentFilterAnimation, kNonInherited);
   TEST_ANIMATION_FLAG(HasCurrentBackdropFilterAnimation, kNonInherited);
-  TEST_ANIMATION_FLAG(IsRunningTransformAnimationOnCompositor, kNonInherited);
-  TEST_ANIMATION_FLAG(IsRunningOpacityAnimationOnCompositor, kNonInherited);
-  TEST_ANIMATION_FLAG(IsRunningFilterAnimationOnCompositor, kNonInherited);
-  TEST_ANIMATION_FLAG(IsRunningBackdropFilterAnimationOnCompositor,
-                      kNonInherited);
   TEST_ANIMATION_FLAG(SubtreeWillChangeContents, kInherited);
+  TEST_ANIMATION_FLAG_NO_DIFF(IsRunningTransformAnimationOnCompositor);
+  TEST_ANIMATION_FLAG_NO_DIFF(IsRunningOpacityAnimationOnCompositor);
+  TEST_ANIMATION_FLAG_NO_DIFF(IsRunningFilterAnimationOnCompositor);
+  TEST_ANIMATION_FLAG_NO_DIFF(IsRunningBackdropFilterAnimationOnCompositor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.cc b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
index ba58e696..9fd59ce 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
@@ -96,7 +96,7 @@
 
 SVGImage::SVGImage(ImageObserver* observer, bool is_multipart)
     : Image(observer, is_multipart),
-      paint_controller_(PaintController::Create()),
+      paint_controller_(std::make_unique<PaintController>()),
       has_pending_timeline_rewind_(false) {}
 
 SVGImage::~SVGImage() {
diff --git a/third_party/blink/renderer/core/workers/global_scope_creation_params.cc b/third_party/blink/renderer/core/workers/global_scope_creation_params.cc
index b899008..30fb8d7 100644
--- a/third_party/blink/renderer/core/workers/global_scope_creation_params.cc
+++ b/third_party/blink/renderer/core/workers/global_scope_creation_params.cc
@@ -18,7 +18,7 @@
     const String& global_scope_name,
     const String& user_agent,
     scoped_refptr<WebWorkerFetchContext> web_worker_fetch_context,
-    const Vector<CSPHeaderAndType>& content_security_policy_parsed_headers,
+    const Vector<CSPHeaderAndType>& outside_content_security_policy_headers,
     network::mojom::ReferrerPolicy referrer_policy,
     const SecurityOrigin* starter_origin,
     bool starter_secure_context,
@@ -71,10 +71,10 @@
       break;
   }
 
-  this->content_security_policy_parsed_headers.ReserveInitialCapacity(
-      content_security_policy_parsed_headers.size());
-  for (const auto& header : content_security_policy_parsed_headers) {
-    this->content_security_policy_parsed_headers.emplace_back(
+  this->outside_content_security_policy_headers.ReserveInitialCapacity(
+      outside_content_security_policy_headers.size());
+  for (const auto& header : outside_content_security_policy_headers) {
+    this->outside_content_security_policy_headers.emplace_back(
         header.first.IsolatedCopy(), header.second);
   }
 
diff --git a/third_party/blink/renderer/core/workers/global_scope_creation_params.h b/third_party/blink/renderer/core/workers/global_scope_creation_params.h
index a147eae..6348c9e 100644
--- a/third_party/blink/renderer/core/workers/global_scope_creation_params.h
+++ b/third_party/blink/renderer/core/workers/global_scope_creation_params.h
@@ -56,7 +56,7 @@
       const String& global_scope_name,
       const String& user_agent,
       scoped_refptr<WebWorkerFetchContext>,
-      const Vector<CSPHeaderAndType>& content_security_policy_parsed_headers,
+      const Vector<CSPHeaderAndType>& outside_content_security_policy_headers,
       network::mojom::ReferrerPolicy referrer_policy,
       const SecurityOrigin*,
       bool starter_secure_context,
@@ -99,7 +99,10 @@
 
   scoped_refptr<WebWorkerFetchContext> web_worker_fetch_context;
 
-  Vector<CSPHeaderAndType> content_security_policy_parsed_headers;
+  // TODO(bashi): This contains "inside" CSP headers for on-the-main-thread
+  // service/shared worker script fetch. Add a separate parameter for "inside"
+  // CSP headers.
+  Vector<CSPHeaderAndType> outside_content_security_policy_headers;
 
   network::mojom::ReferrerPolicy referrer_policy;
   std::unique_ptr<Vector<String>> origin_trial_tokens;
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index 1e8a6b79..d3c87e46 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -458,10 +458,10 @@
                                      creation_params->starter_https_state);
 
   SetOutsideContentSecurityPolicyHeaders(
-      creation_params->content_security_policy_parsed_headers);
+      creation_params->outside_content_security_policy_headers);
   if (csp_apply_mode_ == GlobalScopeCSPApplyMode::kUseCreationParamsCSP) {
     InitContentSecurityPolicyFromVector(
-        creation_params->content_security_policy_parsed_headers);
+        creation_params->outside_content_security_policy_headers);
     BindContentSecurityPolicyToExecutionContext();
   }
 
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
index 11259467..ac5a23d 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
@@ -317,8 +317,7 @@
 
   ContentSecurityPolicy* content_security_policy =
       ContentSecurityPolicy::Create();
-  for (const auto& policy_and_type :
-       outside_content_security_policy_parsed_headers_) {
+  for (const auto& policy_and_type : outside_content_security_policy_headers_) {
     content_security_policy->DidReceiveHeader(
         policy_and_type.first, policy_and_type.second,
         kContentSecurityPolicyHeaderSourceHTTP);
@@ -377,7 +376,7 @@
 
 void WorkerOrWorkletGlobalScope::SetOutsideContentSecurityPolicyHeaders(
     const Vector<CSPHeaderAndType>& headers) {
-  outside_content_security_policy_parsed_headers_ = headers;
+  outside_content_security_policy_headers_ = headers;
 }
 
 void WorkerOrWorkletGlobalScope::InitContentSecurityPolicyFromVector(
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
index bf931f0..3b34db3 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
@@ -184,7 +184,7 @@
 
   // TODO(hiroshige): Pass outsideSettings-CSP via
   // outsideSettings-FetchClientSettingsObject.
-  Vector<CSPHeaderAndType> outside_content_security_policy_parsed_headers_;
+  Vector<CSPHeaderAndType> outside_content_security_policy_headers_;
 
   WorkerReportingProxy& reporting_proxy_;
 
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
index 437ce0b7..40752b7d 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
@@ -94,13 +94,13 @@
   SetReferrerPolicy(creation_params->referrer_policy);
 
   SetOutsideContentSecurityPolicyHeaders(
-      creation_params->content_security_policy_parsed_headers);
+      creation_params->outside_content_security_policy_headers);
 
   // https://drafts.css-houdini.org/worklets/#creating-a-workletglobalscope
   // Step 6: "Invoke the initialize a global object's CSP list algorithm given
   // workletGlobalScope."
   InitContentSecurityPolicyFromVector(
-      creation_params->content_security_policy_parsed_headers);
+      creation_params->outside_content_security_policy_headers);
   BindContentSecurityPolicyToExecutionContext();
 
   OriginTrialContext::AddTokens(this,
diff --git a/third_party/blink/renderer/devtools/front_end/console_counters/WarningErrorCounter.js b/third_party/blink/renderer/devtools/front_end/console_counters/WarningErrorCounter.js
index f23f73e..68cb12e 100644
--- a/third_party/blink/renderer/devtools/front_end/console_counters/WarningErrorCounter.js
+++ b/third_party/blink/renderer/devtools/front_end/console_counters/WarningErrorCounter.js
@@ -10,16 +10,32 @@
   constructor() {
     ConsoleCounters.WarningErrorCounter._instanceForTest = this;
 
+    const countersWrapper = createElement('div');
+    this._toolbarItem = new UI.ToolbarItem(countersWrapper);
+
     this._counter = createElement('div');
     this._counter.addEventListener('click', Common.console.show.bind(Common.console), false);
-    this._toolbarItem = new UI.ToolbarItem(this._counter);
     const shadowRoot = UI.createShadowRootWithCoreStyles(this._counter, 'console_counters/errorWarningCounter.css');
+    countersWrapper.appendChild(this._counter);
+
+    this._violationCounter = createElement('div');
+    this._violationCounter.addEventListener('click', () => {
+      UI.viewManager.showView('audits2');
+    });
+    const violationShadowRoot =
+        UI.createShadowRootWithCoreStyles(this._violationCounter, 'console_counters/errorWarningCounter.css');
+    if (Runtime.experiments.isEnabled('spotlight'))
+      countersWrapper.appendChild(this._violationCounter);
+
 
     this._errors = this._createItem(shadowRoot, 'smallicon-error');
     this._warnings = this._createItem(shadowRoot, 'smallicon-warning');
+    if (Runtime.experiments.isEnabled('spotlight'))
+      this._violations = this._createItem(violationShadowRoot, 'smallicon-info');
     this._titles = [];
     this._errorCount = -1;
     this._warningCount = -1;
+    this._violationCount = -1;
     this._throttler = new Common.Throttler(100);
 
     SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, this._update, this);
@@ -50,14 +66,11 @@
    * @param {!{item: !Element, text: !Element}} item
    * @param {number} count
    * @param {boolean} first
-   * @param {string} title
    */
-  _updateItem(item, count, first, title) {
+  _updateItem(item, count, first) {
     item.item.classList.toggle('hidden', !count);
     item.item.classList.toggle('counter-item-first', first);
     item.text.textContent = count;
-    if (count)
-      this._titles.push(title);
   }
 
   _update() {
@@ -71,26 +84,46 @@
   _updateThrottled() {
     const errors = SDK.consoleModel.errors();
     const warnings = SDK.consoleModel.warnings();
-    if (errors === this._errorCount && warnings === this._warningCount)
+    const violations = SDK.consoleModel.violations();
+    if (errors === this._errorCount && warnings === this._warningCount && violations === this._violationCount)
       return Promise.resolve();
     this._errorCount = errors;
     this._warningCount = warnings;
+    this._violationCount = violations;
 
     this._titles = [];
-    this._toolbarItem.setVisible(!!(errors || warnings));
+    this._counter.classList.toggle('hidden', !(errors || warnings));
+    this._violationCounter.classList.toggle('hidden', !violations);
+    this._toolbarItem.setVisible(!!(errors || warnings || violations));
+
     let errorCountTitle = '';
     if (errors === 1)
       errorCountTitle = ls`${errors} error`;
     else
       errorCountTitle = ls`${errors} errors`;
-    this._updateItem(this._errors, errors, false, errorCountTitle);
+    this._updateItem(this._errors, errors, false);
+    if (errors)
+      this._titles.push(errorCountTitle);
 
     let warningCountTitle = '';
     if (warnings === 1)
       warningCountTitle = ls`${warnings} warning`;
     else
       warningCountTitle = ls`${warnings} warnings`;
-    this._updateItem(this._warnings, warnings, !errors, warningCountTitle);
+    this._updateItem(this._warnings, warnings, !errors);
+    if (warnings)
+      this._titles.push(warningCountTitle);
+
+    if (Runtime.experiments.isEnabled('spotlight')) {
+      let violationCountTitle = '';
+      if (violations === 1)
+        violationCountTitle = ls`${violations} violation`;
+      else
+        violationCountTitle = ls`${violations} violations`;
+      this._updateItem(this._violations, violations, true);
+      this._violationCounter.title = violationCountTitle;
+    }
+
     this._counter.title = this._titles.join(', ');
     UI.inspectorView.toolbarItemResized();
     this._updatingForTest = false;
@@ -105,4 +138,4 @@
   item() {
     return this._toolbarItem;
   }
-};
\ No newline at end of file
+};
diff --git a/third_party/blink/renderer/devtools/front_end/console_counters/module.json b/third_party/blink/renderer/devtools/front_end/console_counters/module.json
index f24e3cf..420d1af 100644
--- a/third_party/blink/renderer/devtools/front_end/console_counters/module.json
+++ b/third_party/blink/renderer/devtools/front_end/console_counters/module.json
@@ -18,4 +18,4 @@
   "resources": [
     "errorWarningCounter.css"
   ]
-}
\ No newline at end of file
+}
diff --git a/third_party/blink/renderer/devtools/front_end/main/Main.js b/third_party/blink/renderer/devtools/front_end/main/Main.js
index 53e24c0a..61a98c40 100644
--- a/third_party/blink/renderer/devtools/front_end/main/Main.js
+++ b/third_party/blink/renderer/devtools/front_end/main/Main.js
@@ -117,6 +117,7 @@
     Runtime.experiments.register('samplingHeapProfilerTimeline', 'Sampling heap profiler timeline', true);
     Runtime.experiments.register('sourceDiff', 'Source diff');
     Runtime.experiments.register('splitInDrawer', 'Split in drawer', true);
+    Runtime.experiments.register('spotlight', 'Spotlight', true);
     Runtime.experiments.register('terminalInDrawer', 'Terminal in drawer', true);
 
     // Timeline
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/ConsoleModel.js b/third_party/blink/renderer/devtools/front_end/sdk/ConsoleModel.js
index 90c3892..a001d3b 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/ConsoleModel.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/ConsoleModel.js
@@ -41,6 +41,7 @@
     this._messageByExceptionId = new Map();
     this._warnings = 0;
     this._errors = 0;
+    this._violations = 0;
     this._pageLoadSequenceNumber = 0;
 
     SDK.targetManager.observeTargets(this);
@@ -305,8 +306,10 @@
    * @param {!SDK.ConsoleMessage} msg
    */
   _incrementErrorWarningCount(msg) {
-    if (msg.source === SDK.ConsoleMessage.MessageSource.Violation)
+    if (msg.source === SDK.ConsoleMessage.MessageSource.Violation) {
+      this._violations++;
       return;
+    }
     switch (msg.level) {
       case SDK.ConsoleMessage.MessageLevel.Warning:
         this._warnings++;
@@ -337,6 +340,7 @@
     this._messageByExceptionId.clear();
     this._errors = 0;
     this._warnings = 0;
+    this._violations = 0;
     this.dispatchEventToListeners(SDK.ConsoleModel.Events.ConsoleCleared);
   }
 
@@ -355,6 +359,13 @@
   }
 
   /**
+   * @return {number}
+   */
+  violations() {
+    return this._violations;
+  }
+
+  /**
    * @param {?SDK.ExecutionContext} currentExecutionContext
    * @param {?SDK.RemoteObject} remoteObject
    */
diff --git a/third_party/blink/renderer/devtools/front_end/worker_service/ServiceDispatcher.js b/third_party/blink/renderer/devtools/front_end/worker_service/ServiceDispatcher.js
index a975d8e..7eb451f 100644
--- a/third_party/blink/renderer/devtools/front_end/worker_service/ServiceDispatcher.js
+++ b/third_party/blink/renderer/devtools/front_end/worker_service/ServiceDispatcher.js
@@ -38,15 +38,16 @@
    * @param {string} data
    */
   _dispatchMessageWrapped(data) {
-    const message = JSON.parse(data);
+    let message;
     try {
+      message = JSON.parse(data);
       if (!(message instanceof Object)) {
         this._sendErrorResponse(message['id'], 'Malformed message');
         return;
       }
       this._dispatchMessage(message);
     } catch (e) {
-      this._sendErrorResponse(message['id'], e.toString() + ' ' + e.stack);
+      this._sendErrorResponse(message ? message['id'] : '', e.toString() + ' ' + e.stack);
     }
   }
 
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_update_ui_event.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_update_ui_event.cc
index 285f99ca..043c29e5 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_update_ui_event.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_update_ui_event.cc
@@ -44,7 +44,7 @@
 ScriptPromise BackgroundFetchUpdateUIEvent::updateUI(
     ScriptState* script_state,
     const BackgroundFetchUIOptions* ui_options) {
-  if (observer_ && !observer_->IsEventActive(script_state)) {
+  if (observer_ && !observer_->IsEventActive()) {
     // Return a rejected promise as the event is no longer active.
     return ScriptPromise::RejectWithDOMException(
         script_state,
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index 6c52673..a7ea572f 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -432,6 +432,7 @@
           "webgpu/gpu.idl",
           "webgpu/gpu_adapter.idl",
           "webgpu/gpu_device.idl",
+          "webgpu/gpu_queue.idl",
           "webmidi/midi_access.idl",
           "webmidi/midi_connection_event.idl",
           "webmidi/midi_input.idl",
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_crypto_config_factory_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_crypto_config_factory_impl.cc
index f0a76a1..40ece3a 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_crypto_config_factory_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_crypto_config_factory_impl.cc
@@ -34,26 +34,26 @@
 
   // ProofVerifier override.
   quic::QuicAsyncStatus VerifyProof(
-      const quic::QuicString& hostname,
+      const std::string& hostname,
       const uint16_t port,
-      const quic::QuicString& server_config,
+      const std::string& server_config,
       quic::QuicTransportVersion transport_version,
       quic::QuicStringPiece chlo_hash,
-      const std::vector<quic::QuicString>& certs,
-      const quic::QuicString& cert_sct,
-      const quic::QuicString& signature,
+      const std::vector<std::string>& certs,
+      const std::string& cert_sct,
+      const std::string& signature,
       const quic::ProofVerifyContext* context,
-      quic::QuicString* error_details,
+      std::string* error_details,
       std::unique_ptr<quic::ProofVerifyDetails>* verify_details,
       std::unique_ptr<quic::ProofVerifierCallback> callback) override {
     return quic::QUIC_SUCCESS;
   }
 
   quic::QuicAsyncStatus VerifyCertChain(
-      const quic::QuicString& hostname,
-      const std::vector<quic::QuicString>& certs,
+      const std::string& hostname,
+      const std::vector<std::string>& certs,
       const quic::ProofVerifyContext* context,
-      quic::QuicString* error_details,
+      std::string* error_details,
       std::unique_ptr<quic::ProofVerifyDetails>* details,
       std::unique_ptr<quic::ProofVerifierCallback> callback) override {
     return quic::QUIC_SUCCESS;
@@ -75,8 +75,8 @@
 
   // ProofSource override.
   void GetProof(const quic::QuicSocketAddress& server_addr,
-                const quic::QuicString& hostname,
-                const quic::QuicString& server_config,
+                const std::string& hostname,
+                const std::string& server_config,
                 quic::QuicTransportVersion transport_version,
                 quic::QuicStringPiece chlo_hash,
                 std::unique_ptr<Callback> callback) override {
@@ -89,15 +89,15 @@
 
   quic::QuicReferenceCountedPointer<Chain> GetCertChain(
       const quic::QuicSocketAddress& server_address,
-      const quic::QuicString& hostname) override {
-    std::vector<quic::QuicString> certs;
+      const std::string& hostname) override {
+    std::vector<std::string> certs;
     certs.push_back("Dummy cert");
     return quic::QuicReferenceCountedPointer<Chain>(
         new quic::ProofSource::Chain(certs));
   }
   void ComputeTlsSignature(
       const quic::QuicSocketAddress& server_address,
-      const quic::QuicString& hostname,
+      const std::string& hostname,
       uint16_t signature_algorithm,
       quic::QuicStringPiece in,
       std::unique_ptr<SignatureCallback> callback) override {
@@ -126,7 +126,7 @@
                                kInputKeyingMaterialLength);
   std::unique_ptr<quic::ProofSource> proof_source(new DummyProofSource);
   return std::make_unique<quic::QuicCryptoServerConfig>(
-      quic::QuicString(source_address_token_secret, kInputKeyingMaterialLength),
+      std::string(source_address_token_secret, kInputKeyingMaterialLength),
       random_generator_, std::move(proof_source),
       quic::KeyExchangeSource::Default(),
       quic::TlsServerHandshaker::CreateSslCtx());
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
index 1b9f13d..241dbc0 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
@@ -181,7 +181,7 @@
                             const quic::QuicSocketAddress& client_address,
                             const quic::QuicSocketAddress& peer_address,
                             const quic::QuicSocketAddress& self_address,
-                            quic::QuicString* error_details) const override {
+                            std::string* error_details) const override {
     return true;
   }
 
@@ -444,7 +444,7 @@
       helper_->GetRandomGenerator()->RandBytes(random_hostname,
                                                kHostnameLength);
       quic::QuicServerId server_id(
-          /*host=*/quic::QuicString(random_hostname, kHostnameLength),
+          /*host=*/std::string(random_hostname, kHostnameLength),
           /*port=*/0,
           /*privacy_mode_enabled=*/false);
       crypto_stream_ = std::make_unique<quic::QuicCryptoClientStream>(
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
index 52d8c2d..dbc1d84 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
@@ -214,7 +214,7 @@
     }
   }
   // If async, packets are queued here to send.
-  quic::QuicDeque<quic::QuicString> packet_queue_;
+  quic::QuicDeque<std::string> packet_queue_;
   // Alarm used to send data asynchronously.
   quic::QuicArenaScopedPtr<quic::QuicAlarm> alarm_;
   // The P2PQuicTransportImpl, which sets itself as the delegate in its
@@ -321,26 +321,26 @@
 
   // ProofVerifier override.
   quic::QuicAsyncStatus VerifyProof(
-      const quic::QuicString& hostname,
+      const std::string& hostname,
       const uint16_t port,
-      const quic::QuicString& server_config,
+      const std::string& server_config,
       quic::QuicTransportVersion transport_version,
       quic::QuicStringPiece chlo_hash,
-      const std::vector<quic::QuicString>& certs,
-      const quic::QuicString& cert_sct,
-      const quic::QuicString& signature,
+      const std::vector<std::string>& certs,
+      const std::string& cert_sct,
+      const std::string& signature,
       const quic::ProofVerifyContext* context,
-      quic::QuicString* error_details,
+      std::string* error_details,
       std::unique_ptr<quic::ProofVerifyDetails>* verify_details,
       std::unique_ptr<quic::ProofVerifierCallback> callback) override {
     return quic::QUIC_FAILURE;
   }
 
   quic::QuicAsyncStatus VerifyCertChain(
-      const quic::QuicString& hostname,
-      const std::vector<quic::QuicString>& certs,
+      const std::string& hostname,
+      const std::vector<std::string>& certs,
       const quic::ProofVerifyContext* context,
-      quic::QuicString* error_details,
+      std::string* error_details,
       std::unique_ptr<quic::ProofVerifyDetails>* details,
       std::unique_ptr<quic::ProofVerifierCallback> callback) override {
     return quic::QUIC_FAILURE;
@@ -359,8 +359,8 @@
 
   // ProofSource override.
   void GetProof(const quic::QuicSocketAddress& server_addr,
-                const quic::QuicString& hostname,
-                const quic::QuicString& server_config,
+                const std::string& hostname,
+                const std::string& server_config,
                 quic::QuicTransportVersion transport_version,
                 quic::QuicStringPiece chlo_hash,
                 std::unique_ptr<Callback> callback) override {
@@ -373,15 +373,15 @@
 
   quic::QuicReferenceCountedPointer<Chain> GetCertChain(
       const quic::QuicSocketAddress& server_address,
-      const quic::QuicString& hostname) override {
-    std::vector<quic::QuicString> certs;
+      const std::string& hostname) override {
+    std::vector<std::string> certs;
     certs.push_back("Test cert");
     return quic::QuicReferenceCountedPointer<Chain>(
         new ProofSource::Chain(certs));
   }
   void ComputeTlsSignature(
       const quic::QuicSocketAddress& server_address,
-      const quic::QuicString& hostname,
+      const std::string& hostname,
       uint16_t signature_algorithm,
       quic::QuicStringPiece in,
       std::unique_ptr<SignatureCallback> callback) override {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_data_channel.cc b/third_party/blink/renderer/modules/peerconnection/rtc_data_channel.cc
index 7c5c2c5c..9597eaa 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_data_channel.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_data_channel.cc
@@ -507,14 +507,14 @@
     }
     if (binary_type_ == kBinaryTypeArrayBuffer) {
       DOMArrayBuffer* dom_buffer = DOMArrayBuffer::Create(
-          buffer->data.data<char>(), SafeCast<unsigned>(buffer->data.size()));
+          buffer->data.cdata(), SafeCast<unsigned>(buffer->data.size()));
       ScheduleDispatchEvent(MessageEvent::Create(dom_buffer));
       return;
     }
     NOTREACHED();
   } else {
     String text =
-        String::FromUTF8(buffer->data.data<char>(), buffer->data.size());
+        String::FromUTF8(buffer->data.cdata<char>(), buffer->data.size());
     if (!text) {
       LOG(ERROR) << "Failed convert received data to UTF16";
       return;
diff --git a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
index 7ff9a42f..8153990 100644
--- a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
+++ b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
@@ -368,6 +368,8 @@
 }
 
 void FetchRespondWithObserver::OnNoResponse() {
+  // TODO(crbug.com/934622): Temporary CHECK for the crash bug.
+  CHECK(GetExecutionContext());
   ServiceWorkerGlobalScopeClient::From(GetExecutionContext())
       ->RespondToFetchEventWithNoResponse(event_id_, event_dispatch_time_,
                                           base::TimeTicks::Now());
diff --git a/third_party/blink/renderer/modules/service_worker/respond_with_observer.cc b/third_party/blink/renderer/modules/service_worker/respond_with_observer.cc
index 1e48691..d1082d4 100644
--- a/third_party/blink/renderer/modules/service_worker/respond_with_observer.cc
+++ b/third_party/blink/renderer/modules/service_worker/respond_with_observer.cc
@@ -24,7 +24,6 @@
 
 void RespondWithObserver::DidDispatchEvent(
     DispatchEventResult dispatch_result) {
-  CHECK(GetExecutionContext());
   if (state_ != kInitial)
     return;
 
@@ -35,21 +34,35 @@
   }
 
   state_ = kDone;
-  observer_.Clear();
 }
 
+// https://w3c.github.io/ServiceWorker/#fetch-event-respondwith
 void RespondWithObserver::RespondWith(ScriptState* script_state,
                                       ScriptPromise script_promise,
                                       ExceptionState& exception_state) {
-  if (state_ != kInitial || !GetExecutionContext()) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kInvalidStateError,
-        "The event has already been responded to.");
+  // 1. `If the dispatch flag is unset, throw an "InvalidStateError"
+  //    DOMException.`
+  if (!observer_->IsDispatchingEvent()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "The event handler is already finished.");
     return;
   }
 
+  // 2. `If the respond-with entered flag is set, throw an "InvalidStateError"
+  //    DOMException.`
+  if (state_ != kInitial) {
+    // Non-initial state during event dispatch means respondWith() was already
+    // called.
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "respondWith() was already called.");
+    return;
+  }
+
+  // 3. `Add r to the extend lifetime promises.`
+  // 4. `Increment the pending promises count by one.`
+  // This is accomplised by WaitUntil().
   state_ = kPending;
-  observer_->WaitUntil(
+  bool will_wait = observer_->WaitUntil(
       script_state, script_promise, exception_state,
       WTF::BindRepeating(&RespondWithObserver::ResponseWasFulfilled,
                          WrapPersistent(this), exception_state.Context(),
@@ -58,13 +71,19 @@
       WTF::BindRepeating(&RespondWithObserver::ResponseWasRejected,
                          WrapPersistent(this),
                          ServiceWorkerResponseError::kPromiseRejected));
+  // If the WaitUntilObserver won't observe the response promise, the event can
+  // end before the response result is reported back to the
+  // ServiceWorkerContextClient, which it doesn't expect (e.g., for fetch
+  // events, RespondToFetchEvent*() must be called before
+  // DidHandleFetchEvent()). So WaitUntilObserver must observe the promise and
+  // call our callbacks before it determines the event is done.
+  DCHECK(will_wait);
 }
 
 void RespondWithObserver::ResponseWasRejected(ServiceWorkerResponseError error,
                                               const ScriptValue& value) {
   OnResponseRejected(error);
   state_ = kDone;
-  observer_.Clear();
 }
 
 void RespondWithObserver::ResponseWasFulfilled(
@@ -74,7 +93,6 @@
     const ScriptValue& value) {
   OnResponseFulfilled(value, context_type, interface_name, property_name);
   state_ = kDone;
-  observer_.Clear();
 }
 
 RespondWithObserver::RespondWithObserver(ExecutionContext* context,
diff --git a/third_party/blink/renderer/modules/service_worker/respond_with_observer.h b/third_party/blink/renderer/modules/service_worker/respond_with_observer.h
index f62f889..1a356dd 100644
--- a/third_party/blink/renderer/modules/service_worker/respond_with_observer.h
+++ b/third_party/blink/renderer/modules/service_worker/respond_with_observer.h
@@ -35,9 +35,10 @@
   void WillDispatchEvent();
   void DidDispatchEvent(DispatchEventResult dispatch_result);
 
-  // The respondWith() observes the promise until the given promise is resolved
-  // or rejected and then delays calling ServiceWorkerGlobalScopeClient::
-  // didHandle*Event() in order to notify the result to the client.
+  // Observes the given promise and calls OnResponseRejected() or
+  // OnResponseFulfilled() when it settles. It also keeps the event alive by
+  // telling the event's WaitUntilObserver to observe the promise. The result of
+  // RespondWith() is therefore reported back before the event finishes.
   void RespondWith(ScriptState*, ScriptPromise, ExceptionState&);
 
   // Called when the respondWith() promise was rejected.
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 3dc73e3..6fd7678 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
@@ -110,7 +110,7 @@
           creation_params->script_url)) {
     // CSP headers, referrer policy, and origin trial tokens will be provided by
     // the InstalledScriptsManager in EvaluateClassicScript().
-    DCHECK(creation_params->content_security_policy_parsed_headers.IsEmpty());
+    DCHECK(creation_params->outside_content_security_policy_headers.IsEmpty());
     DCHECK_EQ(network::mojom::ReferrerPolicy::kDefault,
               creation_params->referrer_policy);
     DCHECK(creation_params->origin_trial_tokens->IsEmpty());
diff --git a/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc b/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
index f510810f..70b7133 100644
--- a/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
+++ b/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
@@ -135,24 +135,27 @@
   MaybeCompleteEvent();
 }
 
-void WaitUntilObserver::WaitUntil(ScriptState* script_state,
+// https://w3c.github.io/ServiceWorker/#dom-extendableevent-waituntil
+bool WaitUntilObserver::WaitUntil(ScriptState* script_state,
                                   ScriptPromise script_promise,
                                   ExceptionState& exception_state,
                                   PromiseSettledCallback on_promise_fulfilled,
                                   PromiseSettledCallback on_promise_rejected) {
   DCHECK_NE(event_dispatch_state_, EventDispatchState::kInitial);
 
-  if (!IsEventActive(script_state)) {
+  // 1. `If the isTrusted attribute is false, throw an "InvalidStateError"
+  // DOMException.`
+  // This might not yet be implemented.
+
+  // 2. `If not active, throw an "InvalidStateError" DOMException.`
+  if (!IsEventActive()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
         "The event handler is already finished and no extend lifetime "
         "promises are outstanding.");
-    return;
+    return false;
   }
 
-  if (!GetExecutionContext())
-    return;
-
   // When handling a notificationclick event, we want to allow one window to
   // be focused or opened. See comments in ::willDispatchEvent(). When
   // waitUntil() is being used, opening or closing a window must happen in a
@@ -163,36 +166,27 @@
                                                    FROM_HERE);
   }
 
+  // 3. `Add f to the extend lifetime promises.`
+  // 4. `Increment the pending promises count by one.`
   IncrementPendingPromiseCount();
   script_promise.Then(
       ThenFunction::CreateFunction(script_state, this, ThenFunction::kFulfilled,
                                    std::move(on_promise_fulfilled)),
       ThenFunction::CreateFunction(script_state, this, ThenFunction::kRejected,
                                    std::move(on_promise_rejected)));
+  return true;
 }
 
-bool WaitUntilObserver::IsEventActive(ScriptState* script_state) const {
-  if (pending_promises_ > 0)
-    return true;
+// https://w3c.github.io/ServiceWorker/#extendableevent-active
+bool WaitUntilObserver::IsEventActive() const {
+  // `An ExtendableEvent object is said to be active when its timed out flag is
+  // unset and either its pending promises count is greater than zero or its
+  // dispatch flag is set.`
+  return pending_promises_ > 0 || IsDispatchingEvent();
+}
 
-  switch (event_dispatch_state_) {
-    case EventDispatchState::kDispatching:
-      // DidDispatchEvent() is called after both the event handler
-      // execution finished and microtasks queued by the event handler execution
-      // finished, it's hard to get the precise time point between the 2
-      // execution phases.
-      // So even in EventDispatchState::kDispatching state at this time point,
-      // running microtask indicates that event handler execution has actually
-      // finished, in such case if there aren't any outstanding extend lifetime
-      // promises.
-      return !v8::MicrotasksScope::IsRunningMicrotasks(
-          script_state->GetIsolate());
-    case EventDispatchState::kInitial:
-    case EventDispatchState::kDispatched:
-    case EventDispatchState::kFailed:
-      return false;
-  }
-  NOTREACHED();
+bool WaitUntilObserver::IsDispatchingEvent() const {
+  return event_dispatch_state_ == EventDispatchState::kDispatching;
 }
 
 WaitUntilObserver::WaitUntilObserver(ExecutionContext* context,
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 a1bd25f2..4a80d125 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
@@ -61,13 +61,19 @@
   void DidDispatchEvent(bool event_dispatch_failed);
 
   // Observes the promise and delays reporting to ServiceWorkerGlobalScopeClient
-  // that the event completed until the given promise is resolved or rejected.
+  // that the event completed until the promise is resolved or rejected.
+  //
   // WaitUntil may be called multiple times. The event is extended until all
   // promises have settled.
+  //
   // If provided, |on_promise_fulfilled| or |on_promise_rejected| is invoked
   // once |script_promise| fulfills or rejects. This enables the caller to do
   // custom handling.
-  void WaitUntil(
+  //
+  // If the event is not active, throws a DOMException and returns false. In
+  // this case the promise is ignored, and |on_promise_fulfilled| and
+  // |on_promise_rejected| will not be called.
+  bool WaitUntil(
       ScriptState*,
       ScriptPromise /* script_promise */,
       ExceptionState&,
@@ -76,7 +82,13 @@
 
   // Whether the associated event is active.
   // https://w3c.github.io/ServiceWorker/#extendableevent-active.
-  bool IsEventActive(ScriptState* script_state) const;
+  bool IsEventActive() const;
+
+  // Whether the event is being dispatched, i.e., the event handler
+  // is being run.
+  // https://dom.spec.whatwg.org/#dispatch-flag
+  // TODO(falken): Can this just use Event::IsBeingDispatched?
+  bool IsDispatchingEvent() const;
 
   void Trace(blink::Visitor*) override;
 
diff --git a/third_party/blink/renderer/modules/vr/vr_display.cc b/third_party/blink/renderer/modules/vr/vr_display.cc
index 1bbfb4a..1e37c86f 100644
--- a/third_party/blink/renderer/modules/vr/vr_display.cc
+++ b/third_party/blink/renderer/modules/vr/vr_display.cc
@@ -50,6 +50,15 @@
 constexpr WTF::TimeDelta kNonImmersivePoseAgeThreshold =
     WTF::TimeDelta::FromMilliseconds(250);
 
+device::mojom::blink::XRFrameDataPtr CreateIdentityFrameData() {
+  auto data = device::mojom::blink::XRFrameData::New();
+  data->pose = device::mojom::blink::VRPose::New();
+  data->pose->orientation.emplace({0.0f, 0.0f, 0.0f, 1.0f});
+  data->pose->position.emplace({0.0f, 0.0f, 0.0f});
+
+  return data;
+}
+
 VREye StringToVREye(const String& which_eye) {
   if (which_eye == "left")
     return kVREyeLeft;
@@ -279,18 +288,28 @@
     DVLOG(2) << __FUNCTION__ << " done: pending_presenting_vsync_="
              << pending_presenting_vsync_;
   } else {
-    // Check if non_immersive_provider_, if not then we are not fully
-    // initialized, or we do not support non-immersive, so don't request the
-    // vsync. If and when non_immersive_provider_ is set it will run this code
-    // again.
-    if (!non_immersive_provider_)
+    // If we haven't been fully initialized yet, then we need to keep waiting
+    // so that we know if we have a non immersive provider or if we need to
+    // pass out identity poses.  When the callback from initialization happens
+    // it will run this code again.
+    if (!non_immersive_session_initialized_)
       return;
     if (pending_non_immersive_vsync_)
       return;
     non_immersive_vsync_waiting_for_pose_.Reset();
     non_immersive_pose_request_time_ = WTF::CurrentTimeTicks();
-    non_immersive_provider_->GetFrameData(WTF::Bind(
-        &VRDisplay::OnNonImmersiveFrameData, WrapWeakPersistent(this)));
+
+    if (non_immersive_provider_) {
+      non_immersive_provider_->GetFrameData(WTF::Bind(
+          &VRDisplay::OnNonImmersiveFrameData, WrapWeakPersistent(this)));
+    } else {
+      // If we don't have a non immersive provider, we should just return
+      // an identity pose.  We're not worried about re-entrant calls right now
+      // because we should end up waiting for the RAF callback which we request
+      // below. If we start to see errors with this, we'll want to do this as a
+      // posted task.
+      OnNonImmersiveFrameData(CreateIdentityFrameData());
+    }
     pending_non_immersive_vsync_ = true;
     pending_non_immersive_vsync_id_ = doc->RequestAnimationFrame(
         MakeGarbageCollected<VRDisplayFrameRequestCallback>(this));
@@ -586,14 +605,20 @@
 
 void VRDisplay::OnNonImmersiveSessionRequestReturned(
     device::mojom::blink::XRSessionPtr session) {
-  if (!session) {
-    // System does not support any kind of session.
-    return;
+  non_immersive_session_initialized_ = true;
+
+  // Only create the non immersive provider if we actually got a session.
+  // If we didn't get a session, we will just hand out identity poses.
+  if (session) {
+    non_immersive_provider_.Bind(std::move(session->data_provider));
+    non_immersive_client_binding_ = MakeGarbageCollected<SessionClientBinding>(
+        this, SessionClientBinding::SessionBindingType::kNonImmersive,
+        std::move(session->client_request));
   }
-  non_immersive_provider_.Bind(std::move(session->data_provider));
-  non_immersive_client_binding_ = MakeGarbageCollected<SessionClientBinding>(
-      this, SessionClientBinding::SessionBindingType::kNonImmersive,
-      std::move(session->client_request));
+
+  // Now that we're initialized, we need to ensure that the data is flowing
+  // by requesting a VSync, since it may have skipped requesting one because
+  // we weren't yet initialized.
   RequestVSync();
 }
 
diff --git a/third_party/blink/renderer/modules/vr/vr_display.h b/third_party/blink/renderer/modules/vr/vr_display.h
index 8c19d5dd..94a20fc 100644
--- a/third_party/blink/renderer/modules/vr/vr_display.h
+++ b/third_party/blink/renderer/modules/vr/vr_display.h
@@ -259,6 +259,7 @@
   bool did_log_getFrameData_ = false;
   bool did_log_requestPresent_ = false;
 
+  bool non_immersive_session_initialized_ = false;
   device::mojom::blink::XRFrameDataProviderPtr non_immersive_provider_;
 
   device::mojom::blink::XRDevicePtr device_ptr_;
diff --git a/third_party/blink/renderer/modules/webdatabase/BUILD.gn b/third_party/blink/renderer/modules/webdatabase/BUILD.gn
index 2f66e4c..cad740db 100644
--- a/third_party/blink/renderer/modules/webdatabase/BUILD.gn
+++ b/third_party/blink/renderer/modules/webdatabase/BUILD.gn
@@ -56,14 +56,16 @@
     "sql_transaction_state.h",
     "sql_transaction_state_machine.cc",
     "sql_transaction_state_machine.h",
+    "sqlite/sandboxed_vfs.cc",
+    "sqlite/sandboxed_vfs.h",
+    "sqlite/sandboxed_vfs_file.cc",
+    "sqlite/sandboxed_vfs_file.h",
     "sqlite/sql_log.h",
     "sqlite/sql_value.cc",
     "sqlite/sql_value.h",
     "sqlite/sqlite_authorizer.cc",
     "sqlite/sqlite_database.cc",
     "sqlite/sqlite_database.h",
-    "sqlite/sqlite_file_system.cc",
-    "sqlite/sqlite_file_system.h",
     "sqlite/sqlite_statement.cc",
     "sqlite/sqlite_statement.h",
     "sqlite/sqlite_transaction.cc",
@@ -77,13 +79,4 @@
     "//sql",
     "//third_party/sqlite",
   ]
-
-  if (is_win) {
-    sources += [ "sqlite/sqlite_file_system_win.cc" ]
-  } else if (is_posix || is_fuchsia) {
-    sources += [ "sqlite/sqlite_file_system_posix.cc" ]
-  }
-
-  # Expose chromium_sqlite3_* functions from Chromium's patched SQLite.
-  defines = [ "CHROMIUM_SQLITE_INTERNALS" ]  # So that sqlite3.h is not included without this set.
 }
diff --git a/third_party/blink/renderer/modules/webdatabase/database_tracker.cc b/third_party/blink/renderer/modules/webdatabase/database_tracker.cc
index 6a33c79..14b3544 100644
--- a/third_party/blink/renderer/modules/webdatabase/database_tracker.cc
+++ b/third_party/blink/renderer/modules/webdatabase/database_tracker.cc
@@ -43,7 +43,6 @@
 #include "third_party/blink/renderer/modules/webdatabase/database_client.h"
 #include "third_party/blink/renderer/modules/webdatabase/database_context.h"
 #include "third_party/blink/renderer/modules/webdatabase/quota_tracker.h"
-#include "third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
@@ -67,7 +66,6 @@
 }
 
 DatabaseTracker::DatabaseTracker() {
-  SQLiteFileSystem::InitializeSQLite();
 }
 
 bool DatabaseTracker::CanEstablishDatabase(DatabaseContext* database_context,
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/DEPS b/third_party/blink/renderer/modules/webdatabase/sqlite/DEPS
index 1a2948b..0bdaea5f 100644
--- a/third_party/blink/renderer/modules/webdatabase/sqlite/DEPS
+++ b/third_party/blink/renderer/modules/webdatabase/sqlite/DEPS
@@ -1,4 +1,6 @@
 include_rules = [
+    "+base/files/file.h",
+    "+base/threading/platform_thread.h",
     "+sql/initialization.h",
     "+third_party/sqlite/sqlite3.h",
 ]
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.cc b/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.cc
new file mode 100644
index 0000000..92bfd37
--- /dev/null
+++ b/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.cc
@@ -0,0 +1,319 @@
+// 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/webdatabase/sqlite/sandboxed_vfs.h"
+
+#include <algorithm>
+#include <cstring>
+#include <string>
+#include <utility>
+
+#include "base/files/file.h"
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+#include "sql/initialization.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs_file.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/sqlite/sqlite3.h"
+
+#if OS_WIN
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+namespace blink {
+
+namespace {
+
+// Name used to register the VFS with SQLite. Must be unique within Chrome.
+constexpr char kSqliteVfsName[] = "renderer_sandboxed_vfs";
+
+// Extracts the SandboxedVfs* stashed in a SQLite VFS structure.
+SandboxedVfs* SandboxedVfsFromSqliteVfs(sqlite3_vfs* vfs) {
+  DCHECK(vfs);
+  return reinterpret_cast<SandboxedVfs*>(vfs->pAppData);
+}
+
+int SandboxedVfsOpen(sqlite3_vfs* vfs,
+                     const char* full_path,
+                     sqlite3_file* result_file,
+                     int requested_flags,
+                     int* granted_flags) {
+  return SandboxedVfsFromSqliteVfs(vfs)->Open(full_path, result_file,
+                                              requested_flags, granted_flags);
+}
+int SandboxedVfsDelete(sqlite3_vfs* vfs, const char* full_path, int sync_dir) {
+  return SandboxedVfsFromSqliteVfs(vfs)->Delete(full_path, sync_dir);
+}
+int SandboxedVfsAccess(sqlite3_vfs* vfs,
+                       const char* full_path,
+                       int flags,
+                       int* result) {
+  return SandboxedVfsFromSqliteVfs(vfs)->Access(full_path, flags, result);
+}
+int SandboxedVfsFullPathname(sqlite3_vfs* vfs,
+                             const char* file_path,
+                             int result_size,
+                             char* result) {
+  return SandboxedVfsFromSqliteVfs(vfs)->FullPathname(file_path, result_size,
+                                                      result);
+}
+int SandboxedVfsRandomness(sqlite3_vfs* vfs, int result_size, char* result) {
+  return SandboxedVfsFromSqliteVfs(vfs)->Randomness(result_size, result);
+}
+int SandboxedVfsSleep(sqlite3_vfs* vfs, int microseconds) {
+  return SandboxedVfsFromSqliteVfs(vfs)->Sleep(microseconds);
+}
+int SandboxedVfsGetLastError(sqlite3_vfs* vfs,
+                             int message_size,
+                             char* message) {
+  return SandboxedVfsFromSqliteVfs(vfs)->GetLastError(message_size, message);
+}
+int SandboxedVfsCurrentTimeInt64(sqlite3_vfs* vfs, sqlite3_int64* result_ms) {
+  return SandboxedVfsFromSqliteVfs(vfs)->CurrentTimeInt64(result_ms);
+}
+
+sqlite3_vfs SqliteVfsFor(SandboxedVfs* sandboxed_vfs) {
+  DCHECK_EQ(sandboxed_vfs, reinterpret_cast<SandboxedVfs*>(
+                               reinterpret_cast<void*>(sandboxed_vfs)))
+      << "This implementation round-trips SandboxedVfs* via void*";
+
+  // VFS API entry points are listed at https://www.sqlite.org/c3ref/vfs.html
+  static constexpr int kSqliteVfsApiVersion = 3;
+
+  // Maximum file path size.
+  // TODO(pwnall): Obtain this from //base or some other good place.
+  static constexpr int kSqliteMaxPathSize = 512;
+
+  return {
+      kSqliteVfsApiVersion,
+      sizeof(SandboxedVfsFileSqliteBridge),
+      kSqliteMaxPathSize,
+      /*pNext=*/nullptr,
+      kSqliteVfsName,
+      /*pAppData=*/reinterpret_cast<void*>(sandboxed_vfs),
+      SandboxedVfsOpen,
+      SandboxedVfsDelete,
+      SandboxedVfsAccess,
+      SandboxedVfsFullPathname,
+      /*xDlOpen=*/nullptr,
+      /*xDlError=*/nullptr,
+      /*xDlSym=*/nullptr,
+      /*xDlClose=*/nullptr,
+      SandboxedVfsRandomness,
+      SandboxedVfsSleep,
+      /*xCurrentTime=*/nullptr,  // Deprecated in API versions 2 and above.
+      SandboxedVfsGetLastError,
+      SandboxedVfsCurrentTimeInt64,
+      /*xSetSystemCall=*/nullptr,
+      /*xGetSystemCall=*/nullptr,
+      /*xNextSystemCall=*/nullptr,
+  };
+}
+
+// Converts a SQLite full file path to a Blink string.
+//
+// The argument is guaranteed to be the result of a FullPathname() call, with
+// an optional suffix. The suffix always starts with "-".
+String StringFromFullPath(const char* full_path) {
+  DCHECK(full_path);
+  return String::FromUTF8(full_path);
+}
+
+// SQLite measures time according to the Julian calendar.
+WTF::Time SqliteEpoch() {
+  constexpr const double kMicroSecondsPerDay = 24 * 60 * 60 * 1000;
+  // The ".5" is intentional -- days in the Julian calendar start at noon.
+  // The offset is in the SQLite source code (os_unix.c) multiplied by 10.
+  constexpr const double kUnixEpochAsJulianDay = 2440587.5;
+
+  return WTF::Time::FromJsTime(-kUnixEpochAsJulianDay * kMicroSecondsPerDay);
+}
+
+}  // namespace
+
+// static
+SandboxedVfs& SandboxedVfs::GetInstance() {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(SandboxedVfs, instance, ());
+  return instance;
+}
+
+SandboxedVfs::SandboxedVfs()
+    : sandboxed_vfs_(SqliteVfsFor(this)),
+      sqlite_epoch_(SqliteEpoch()),
+      platform_(Platform::Current()),
+      last_error_(base::File::FILE_OK) {
+  sql::EnsureSqliteInitialized();
+
+  // The register function returns a SQLite status as an int. The status is
+  // ignored here. If registration fails, we'd want to report the error while
+  // attempting to open a database. This is exactly what will happen, because
+  // SQLite won't find the VFS we're asking for.
+  sqlite3_vfs_register(&sandboxed_vfs_, /*make_default=*/0);
+}
+
+std::tuple<int, sqlite3*> SandboxedVfs::OpenDatabase(const String& filename) {
+  sqlite3* connection;
+  constexpr int open_flags =
+      SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_PRIVATECACHE;
+  int status = sqlite3_open_v2(filename.Utf8().data(), &connection, open_flags,
+                               kSqliteVfsName);
+  if (status != SQLITE_OK) {
+    // SQLite creates a connection handle in most cases where open fails.
+    if (connection) {
+      sqlite3_close(connection);
+      connection = nullptr;
+    }
+  }
+  return {status, connection};
+}
+
+int SandboxedVfs::Open(const char* full_path,
+                       sqlite3_file* result_file,
+                       int requested_flags,
+                       int* granted_flags) {
+  DCHECK(full_path) << "WebSQL does not support creating temporary file names";
+  DCHECK(result_file);
+  DCHECK_EQ(0, requested_flags & SQLITE_OPEN_DELETEONCLOSE)
+      << "SQLITE_OPEN_DELETEONCLOSE should not be used by WebSQL";
+  DCHECK_EQ(0, requested_flags & SQLITE_OPEN_EXCLUSIVE)
+      << "SQLITE_OPEN_EXCLUSIVE should not be used by WebSQL";
+
+  String file_name = StringFromFullPath(full_path);
+
+  // TODO(pwnall): This doesn't have to be synchronous. WebSQL's open sequence
+  //               is asynchronous, so we could open all the needed files (DB,
+  //               journal, etc.) asynchronously, and store them in a hash table
+  //               that would be used here.
+  base::File file = platform_->DatabaseOpenFile(file_name, requested_flags);
+
+  if (!file.IsValid()) {
+    // TODO(pwnall): Figure out if we can remove the fallback to read-only.
+    if (!(requested_flags & SQLITE_OPEN_READWRITE)) {
+      // The SQLite API requires that pMethods is set to null even if the open
+      // call returns a failure status.
+      result_file->pMethods = nullptr;
+      return SQLITE_CANTOPEN;
+    }
+
+    int new_flags =
+        (requested_flags & ~SQLITE_OPEN_READWRITE) | SQLITE_OPEN_READONLY;
+    return Open(full_path, result_file, new_flags, granted_flags);
+  }
+
+  SandboxedVfsFile::Create(std::move(file), std::move(file_name), this,
+                           result_file);
+  if (granted_flags)
+    *granted_flags = requested_flags;
+  return SQLITE_OK;
+}
+
+int SandboxedVfs::Delete(const char* full_path, int sync_dir) {
+  DCHECK(full_path);
+  return platform_->DatabaseDeleteFile(StringFromFullPath(full_path), sync_dir);
+}
+
+int SandboxedVfs::Access(const char* full_path, int flags, int* result) {
+  DCHECK(full_path);
+  DCHECK(result);
+  long attributes =
+      platform_->DatabaseGetFileAttributes(StringFromFullPath(full_path));
+
+  // TODO(pwnall): Make the mojo interface portable across OSes, instead of
+  //               messing around with OS-dependent constants here.
+
+#if defined(OS_WIN)
+  const bool file_exists =
+      static_cast<DWORD>(attributes) != INVALID_FILE_ATTRIBUTES;
+#else
+  const bool file_exists = attributes >= 0;
+#endif  // defined(OS_WIN)
+
+  if (!file_exists) {
+    *result = 0;
+    return SQLITE_OK;
+  }
+
+#if defined(OS_WIN)
+  const bool can_read = true;
+  const bool can_write = (attributes & FILE_ATTRIBUTE_READONLY) == 0;
+#else
+  const bool can_read = (attributes & R_OK) != 0;
+  const bool can_write = (attributes & W_OK) != 0;
+#endif  // defined(OS_WIN)
+
+  switch (flags) {
+    case SQLITE_ACCESS_EXISTS:
+      *result = 1;
+      break;
+    case SQLITE_ACCESS_READ:
+      *result = can_read ? 1 : 0;
+      break;
+    case SQLITE_ACCESS_READWRITE:
+      *result = (can_read && can_write) ? 1 : 0;
+      break;
+    default:
+      NOTREACHED() << "Unsupported xAccess flags: " << flags;
+      return SQLITE_ERROR;
+  }
+  return SQLITE_OK;
+}
+
+int SandboxedVfs::FullPathname(const char* file_path,
+                               int result_size,
+                               char* result) {
+  DCHECK(file_path);
+  DCHECK_GT(result_size, 0);
+  DCHECK(result);
+
+  // Renderer processes cannot access files directly, so it doesn't make sense
+  // to expose full paths here.
+  size_t file_path_size = std::strlen(file_path) + 1;
+  if (static_cast<size_t>(result_size) < file_path_size)
+    return SQLITE_CANTOPEN;
+  std::memcpy(result, file_path, file_path_size);
+  return SQLITE_OK;
+}
+
+int SandboxedVfs::Randomness(int result_size, char* result) {
+  DCHECK_GT(result_size, 0);
+  DCHECK(result);
+
+  // TODO(pwnall): Figure out if we need a real implementation.
+  std::memset(result, 0, result_size);
+  return result_size;
+}
+
+int SandboxedVfs::Sleep(int microseconds) {
+  DCHECK_GE(microseconds, 0);
+  base::PlatformThread::Sleep(WTF::TimeDelta::FromMicroseconds(microseconds));
+  return SQLITE_OK;
+}
+
+int SandboxedVfs::GetLastError(int message_size, char* message) const {
+  DCHECK_GE(message_size, 0);
+  DCHECK(message_size == 0 || message);
+
+  std::string error_string = base::File::ErrorToString(last_error_);
+  size_t error_string_size = error_string.length() + 1;
+  size_t copy_length =
+      std::min(static_cast<size_t>(message_size), error_string_size);
+  std::memcpy(message, error_string.c_str(), copy_length);
+  // The return value is zero if the message fits in the buffer, and non-zero if
+  // it does not fit.
+  return copy_length != error_string_size;
+}
+
+int SandboxedVfs::CurrentTimeInt64(sqlite3_int64* result_ms) {
+  DCHECK(result_ms);
+
+  WTF::TimeDelta delta = WTF::Time::Now() - sqlite_epoch_;
+  *result_ms = delta.InMilliseconds();
+  return SQLITE_OK;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.h b/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.h
new file mode 100644
index 0000000..366ade9e
--- /dev/null
+++ b/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.h
@@ -0,0 +1,71 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBDATABASE_SQLITE_SANDBOXED_VFS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBDATABASE_SQLITE_SANDBOXED_VFS_H_
+
+#include <tuple>
+
+#include "base/files/file.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/sqlite/sqlite3.h"
+
+namespace blink {
+
+class Platform;
+
+// SQLite VFS implementation that works in the Chrome renderer sandbox.
+//
+// Instances are thread-friendly, and expected to be used on Blink's database
+// thread.
+class SandboxedVfs {
+  USING_FAST_MALLOC(SandboxedVfs);
+
+ public:
+  // Factory method for the singleton instance.
+  static SandboxedVfs& GetInstance();
+
+  ~SandboxedVfs() = delete;
+
+  // Opens a database file.
+  //
+  // Returns a SQLite status and a SQLite connection. If the status is not
+  // SQLITE_OK, the returned connection is null.
+  std::tuple<int, sqlite3*> OpenDatabase(const String& filename);
+
+  // sqlite3_vfs implementation.
+  int Open(const char* full_path,
+           sqlite3_file* result_file,
+           int requested_flags,
+           int* granted_flags);
+  int Delete(const char* full_path, int sync_dir);
+  int Access(const char* full_path, int flags, int* result);
+  int FullPathname(const char* file_path, int result_size, char* result);
+  int Randomness(int result_size, char* result);
+  int Sleep(int microseconds);
+  int GetLastError(int message_size, char* message) const;
+  int CurrentTimeInt64(sqlite3_int64* result_ms);
+
+  // Used by SandboxedVfsFile.
+  Platform* GetPlatform() { return platform_; }
+  void SetLastError(base::File::Error error) { this->last_error_ = error; }
+
+ private:
+  // Use GetInstance() instead of constructing this directly.
+  SandboxedVfs();
+
+  // Registers the VFS with SQLite. Failures are silently ignored.
+  void RegisterVfs();
+
+  sqlite3_vfs sandboxed_vfs_;
+  const WTF::Time sqlite_epoch_;
+  Platform* const platform_;
+  base::File::Error last_error_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBDATABASE_SQLITE_SANDBOXED_VFS_H_
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs_file.cc b/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs_file.cc
new file mode 100644
index 0000000..2fd7f1815
--- /dev/null
+++ b/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs_file.cc
@@ -0,0 +1,532 @@
+// 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/webdatabase/sqlite/sandboxed_vfs_file.h"
+
+#include <atomic>
+#include <cstring>
+#include <type_traits>
+#include <utility>
+
+#include "base/files/file.h"
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+#include "sql/initialization.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.h"
+#include "third_party/sqlite/sqlite3.h"
+
+namespace blink {
+
+namespace {
+
+int SandboxedClose(sqlite3_file* file) {
+  return SandboxedVfsFile::FromSqliteFile(file)->Close();
+}
+int SandboxedRead(sqlite3_file* file,
+                  void* buffer,
+                  int size,
+                  sqlite3_int64 offset) {
+  return SandboxedVfsFile::FromSqliteFile(file)->Read(buffer, size, offset);
+}
+int SandboxedWrite(sqlite3_file* file,
+                   const void* buffer,
+                   int size,
+                   sqlite3_int64 offset) {
+  return SandboxedVfsFile::FromSqliteFile(file)->Write(buffer, size, offset);
+}
+int SandboxedTruncate(sqlite3_file* file, sqlite3_int64 size) {
+  return SandboxedVfsFile::FromSqliteFile(file)->Truncate(size);
+}
+int SandboxedSync(sqlite3_file* file, int flags) {
+  return SandboxedVfsFile::FromSqliteFile(file)->Sync(flags);
+}
+int SandboxedFileSize(sqlite3_file* file, sqlite3_int64* result_size) {
+  return SandboxedVfsFile::FromSqliteFile(file)->FileSize(result_size);
+}
+int SandboxedLock(sqlite3_file* file, int mode) {
+  return SandboxedVfsFile::FromSqliteFile(file)->Lock(mode);
+}
+int SandboxedUnlock(sqlite3_file* file, int mode) {
+  return SandboxedVfsFile::FromSqliteFile(file)->Unlock(mode);
+}
+int SandboxedCheckReservedLock(sqlite3_file* file, int* has_reserved_lock) {
+  return SandboxedVfsFile::FromSqliteFile(file)->CheckReservedLock(
+      has_reserved_lock);
+}
+int SandboxedFileControl(sqlite3_file* file, int opcode, void* data) {
+  return SandboxedVfsFile::FromSqliteFile(file)->FileControl(opcode, data);
+}
+int SandboxedSectorSize(sqlite3_file* file) {
+  return SandboxedVfsFile::FromSqliteFile(file)->SectorSize();
+}
+int SandboxedDeviceCharacteristics(sqlite3_file* file) {
+  return SandboxedVfsFile::FromSqliteFile(file)->DeviceCharacteristics();
+}
+int SandboxedShmMap(sqlite3_file* file,
+                    int page_index,
+                    int page_size,
+                    int extend_file_if_needed,
+                    void volatile** result) {
+  return SandboxedVfsFile::FromSqliteFile(file)->ShmMap(
+      page_index, page_size, extend_file_if_needed, result);
+}
+int SandboxedShmLock(sqlite3_file* file, int offset, int size, int flags) {
+  return SandboxedVfsFile::FromSqliteFile(file)->ShmLock(offset, size, flags);
+}
+void SandboxedShmBarrier(sqlite3_file* file) {
+  SandboxedVfsFile::FromSqliteFile(file)->ShmBarrier();
+}
+int SandboxedShmUnmap(sqlite3_file* file, int also_delete_file) {
+  return SandboxedVfsFile::FromSqliteFile(file)->ShmUnmap(also_delete_file);
+}
+int SandboxedFetch(sqlite3_file* file,
+                   sqlite3_int64 offset,
+                   int size,
+                   void** result) {
+  return SandboxedVfsFile::FromSqliteFile(file)->Fetch(offset, size, result);
+}
+int SandboxedUnfetch(sqlite3_file* file,
+                     sqlite3_int64 offset,
+                     void* fetch_result) {
+  return SandboxedVfsFile::FromSqliteFile(file)->Unfetch(offset, fetch_result);
+}
+
+const sqlite3_io_methods* GetSqliteIoMethods() {
+  // VFS IO API entry points are listed at
+  // https://www.sqlite.org/c3ref/io_methods.html
+  static constexpr int kSqliteVfsIoApiVersion = 3;
+
+  static const sqlite3_io_methods kIoMethods = {
+      kSqliteVfsIoApiVersion,
+      SandboxedClose,
+      SandboxedRead,
+      SandboxedWrite,
+      SandboxedTruncate,
+      SandboxedSync,
+      SandboxedFileSize,
+      SandboxedLock,
+      SandboxedUnlock,
+      SandboxedCheckReservedLock,
+      SandboxedFileControl,
+      SandboxedSectorSize,
+      SandboxedDeviceCharacteristics,
+      SandboxedShmMap,
+      SandboxedShmLock,
+      SandboxedShmBarrier,
+      SandboxedShmUnmap,
+      SandboxedFetch,
+      SandboxedUnfetch,
+  };
+
+  return &kIoMethods;
+}
+
+}  // namespace
+
+// static
+void SandboxedVfsFile::Create(base::File file,
+                              String file_name,
+                              SandboxedVfs* vfs,
+                              sqlite3_file* buffer) {
+  SandboxedVfsFileSqliteBridge* bridge =
+      SandboxedVfsFileSqliteBridge::FromSqliteFile(buffer);
+  bridge->blink_file =
+      new SandboxedVfsFile(std::move(file), std::move(file_name), vfs);
+  bridge->sqlite_file.pMethods = GetSqliteIoMethods();
+}
+
+// static
+SandboxedVfsFile* SandboxedVfsFile::FromSqliteFile(sqlite3_file* sqlite_file) {
+  return SandboxedVfsFileSqliteBridge::FromSqliteFile(sqlite_file)->blink_file;
+}
+
+int SandboxedVfsFile::Close() {
+  file_.Close();
+  return SQLITE_OK;
+}
+
+int SandboxedVfsFile::Read(void* buffer, int size, sqlite3_int64 offset) {
+  DCHECK(buffer);
+  DCHECK_GE(size, 0);
+  DCHECK_GE(offset, 0);
+  char* data = reinterpret_cast<char*>(buffer);
+
+  // If we supported mmap()ed files, we'd check for a memory mapping here,
+  // and try to fill as much of the request as possible from the mmap()ed
+  // region.
+
+  int bytes_read = file_.Read(offset, data, size);
+  DCHECK_LE(bytes_read, size);
+  if (bytes_read == size)
+    return SQLITE_OK;
+
+  // SQLite first reads the database header without locking the file. On
+  // Windows, this read will fail if there is an exclusive lock on the file,
+  // even if the current process owns that lock.
+  if (sqlite_lock_mode_ == SQLITE_LOCK_NONE) {
+    // The unlocked read is considered an optimization. SQLite can continue even
+    // if the read fails, as long as failure is communicated by zeroing out the
+    // output buffer.
+    std::memset(data, 0, size);
+    return SQLITE_OK;
+  }
+
+  if (bytes_read < 0) {
+    vfs_->SetLastError(base::File::GetLastFileError());
+    return SQLITE_IOERR_READ;
+  }
+
+  // SQLite requires that we fill the unread bytes in the buffer with zeros.
+  std::memset(data + bytes_read, 0, size - bytes_read);
+  return SQLITE_IOERR_SHORT_READ;
+}
+
+int SandboxedVfsFile::Write(const void* buffer,
+                            int size,
+                            sqlite3_int64 offset) {
+  DCHECK(buffer);
+  DCHECK_GE(size, 0);
+  DCHECK_GE(offset, 0);
+  const char* data = reinterpret_cast<const char*>(buffer);
+
+  // If we supported mmap()ed files, we'd check for a memory mapping here,
+  // and try to fill as much of the request as possible by copying to the
+  // mmap()ed region.
+
+  int bytes_written = file_.Write(offset, data, size);
+  DCHECK_LE(bytes_written, size);
+  if (bytes_written >= size)
+    return SQLITE_OK;
+
+  base::File::Error last_error = base::File::GetLastFileError();
+  vfs_->SetLastError(last_error);
+  if (last_error == base::File::Error::FILE_ERROR_NO_SPACE)
+    return SQLITE_FULL;
+
+  return SQLITE_IOERR_WRITE;
+}
+
+int SandboxedVfsFile::Truncate(sqlite3_int64 size) {
+  if (file_.SetLength(size))
+    return SQLITE_OK;
+
+  // On Mac and Linux, the renderer sandbox blocks ftruncate(), so we have to
+  // use a sync mojo IPC to ask the browser process to call ftruncate() for us.
+  //
+  // TODO(pwnall): Figure out if we can allow ftruncate() in the renderer. It
+  //               would be useful for low-level storage APIs, like the upcoming
+  //               filesystem API.
+  if (!vfs_->GetPlatform()->DatabaseSetFileSize(file_name_, size))
+    return SQLITE_IOERR_TRUNCATE;
+
+  return SQLITE_OK;
+}
+
+int SandboxedVfsFile::Sync(int flags) {
+  // NOTE: SQLite passes in (SQLITE_SYNC_NORMAL or SQLITE_SYNC_FULL),
+  //       potentially OR-ed with SQLITE_SYNC_DATAONLY. Implementing these could
+  //       lead to better performance.
+  if (!file_.Flush()) {
+    vfs_->SetLastError(base::File::GetLastFileError());
+    return SQLITE_IOERR_FSYNC;
+  }
+
+  // The unix VFS also syncs the file's directory on the first xSync() call.
+  // Chrome's LevelDB Env implementation does the same for specific files
+  // (database manifests).
+  //
+  // For WebSQL, we would want to sync the directory at file open time, when the
+  // file is opened for writing.
+
+  return SQLITE_OK;
+}
+
+int SandboxedVfsFile::FileSize(sqlite3_int64* result_size) {
+  int64_t length = file_.GetLength();
+  if (length < 0) {
+    vfs_->SetLastError(base::File::GetLastFileError());
+    return SQLITE_IOERR_FSTAT;
+  }
+
+  // SQLite's unix VFS reports 1-byte files as empty. This is documented as a
+  // workaround for a fairly obscure bug. See unixFileSize() in os_unix.c.
+  if (length == 1)
+    length = 0;
+
+  *result_size = length;
+  return SQLITE_OK;
+}
+
+namespace {
+
+// True if our simplified implementation uses an exclusive lock for a mode.
+bool IsExclusiveLockMode(int sqlite_lock_mode) {
+  switch (sqlite_lock_mode) {
+    case SQLITE_LOCK_NONE:
+    case SQLITE_LOCK_SHARED:
+      return false;
+
+    case SQLITE_LOCK_RESERVED:
+    case SQLITE_LOCK_PENDING:
+    case SQLITE_LOCK_EXCLUSIVE:
+      return true;
+  }
+
+  NOTREACHED() << "Unsupported mode: " << sqlite_lock_mode;
+  return false;
+}
+
+}  // namespace
+
+int SandboxedVfsFile::Lock(int mode) {
+#if defined(OS_FUCHSIA)
+  return SQLITE_IOERR_LOCK;
+#else
+  base::File::LockMode file_lock_mode = base::File::LockMode::kExclusive;
+
+  switch (mode) {
+    case SQLITE_LOCK_NONE:
+      return SQLITE_OK;
+
+    case SQLITE_LOCK_SHARED:
+      if (sqlite_lock_mode_ != SQLITE_LOCK_NONE)
+        return SQLITE_OK;
+
+      file_lock_mode = base::File::LockMode::kShared;
+      break;
+
+    case SQLITE_LOCK_RESERVED:
+      // A SHARED lock is required before a RESERVED lock is acquired.
+      DCHECK_EQ(sqlite_lock_mode_, SQLITE_LOCK_SHARED);
+      file_lock_mode = base::File::LockMode::kExclusive;
+      break;
+
+    case SQLITE_LOCK_PENDING:
+      // A SHARED lock is required before a PENDING lock is acquired. The caller
+      // may have a RESERVED lock.
+      if (sqlite_lock_mode_ == SQLITE_LOCK_RESERVED) {
+        sqlite_lock_mode_ = mode;
+        return SQLITE_OK;
+      }
+
+      DCHECK_EQ(sqlite_lock_mode_, SQLITE_LOCK_SHARED);
+      file_lock_mode = base::File::LockMode::kExclusive;
+      break;
+
+    case SQLITE_LOCK_EXCLUSIVE:
+      if (IsExclusiveLockMode(sqlite_lock_mode_)) {
+        sqlite_lock_mode_ = mode;
+        return SQLITE_OK;
+      }
+      file_lock_mode = base::File::LockMode::kExclusive;
+      break;
+
+    default:
+      NOTREACHED() << "Unimplemented xLock() mode: " << mode;
+  }
+
+  DCHECK_EQ(IsExclusiveLockMode(mode),
+            file_lock_mode == base::File::LockMode::kExclusive)
+      << "Incorrect file_lock_mode logic for SQLite mode: " << mode;
+
+  // On POSIX, it would be possible to upgrade atomically from a shared lock to
+  // an exclusive lock. This implementation prioritizes the simplicity of no
+  // platform-specific code over being faster in high contention cases.
+  if (sqlite_lock_mode_ != SQLITE_LOCK_NONE) {
+    base::File::Error error = file_.Unlock();
+    if (error != base::File::FILE_OK) {
+      vfs_->SetLastError(base::File::GetLastFileError());
+      return SQLITE_IOERR_LOCK;
+    }
+    sqlite_lock_mode_ = SQLITE_LOCK_NONE;
+  }
+
+  base::File::Error error = file_.Lock(file_lock_mode);
+  if (error != base::File::FILE_OK) {
+    vfs_->SetLastError(base::File::GetLastFileError());
+    return SQLITE_IOERR_LOCK;
+  }
+
+  sqlite_lock_mode_ = mode;
+  return SQLITE_OK;
+#endif  // defined(OS_FUCHSIA)
+}
+
+int SandboxedVfsFile::Unlock(int mode) {
+  // No-op if we're already unlocked or at the requested mode.
+  if (sqlite_lock_mode_ == mode || sqlite_lock_mode_ == SQLITE_LOCK_NONE)
+    return SQLITE_OK;
+
+#if defined(OS_FUCHSIA)
+  return SQLITE_IOERR_UNLOCK;
+#else
+  // On POSIX, it is possible to downgrade atomically from an exclusive lock to
+  // a shared lock. SQLite's unix VFS takes advantage of this. This
+  // implementation prioritizes the simplicity of no platform-specific code over
+  // being faster in high contention cases.
+  base::File::Error error = file_.Unlock();
+  if (error != base::File::FILE_OK) {
+    vfs_->SetLastError(base::File::GetLastFileError());
+    return SQLITE_IOERR_UNLOCK;
+  }
+
+  if (mode == SQLITE_LOCK_NONE) {
+    sqlite_lock_mode_ = mode;
+    return SQLITE_OK;
+  }
+
+  DCHECK_EQ(mode, SQLITE_LOCK_SHARED);
+  error = file_.Lock(base::File::LockMode::kShared);
+  if (error == base::File::FILE_OK) {
+    sqlite_lock_mode_ = mode;
+    return SQLITE_OK;
+  }
+
+  // Gave up the exclusive lock, but failed to get a shared lock.
+  vfs_->SetLastError(base::File::GetLastFileError());
+  sqlite_lock_mode_ = SQLITE_LOCK_NONE;
+  return SQLITE_IOERR_UNLOCK;
+#endif  // defined(OS_FUCHSIA)
+}
+
+int SandboxedVfsFile::CheckReservedLock(int* has_reserved_lock) {
+  if (IsExclusiveLockMode(sqlite_lock_mode_)) {
+    *has_reserved_lock = 1;
+    return SQLITE_OK;
+  }
+
+  if (sqlite_lock_mode_ == SQLITE_LOCK_SHARED) {
+    // Lock modes at or above RESERVED map to exclusive locks in our simplified
+    // implementation. If this process has a shared lock, no other process can
+    // have an exclusive lock.
+    *has_reserved_lock = 0;
+    return SQLITE_OK;
+  }
+
+#if defined(OS_FUCHSIA)
+  return SQLITE_IOERR_CHECKRESERVEDLOCK;
+#else
+  // On POSIX, it's possible to query the existing lock state of a file. The
+  // SQLite unix VFS takes advantage of this. On Windows, this isn't the case.
+  // Follow the strategy of the Windows VFS, which checks by trying to get an
+  // exclusive lock on the file.
+  base::File::Error error = file_.Lock(base::File::LockMode::kShared);
+  if (error != base::File::FILE_OK) {
+    *has_reserved_lock = 1;
+    return SQLITE_OK;
+  }
+
+  *has_reserved_lock = 0;
+  if (file_.Unlock() == base::File::FILE_OK)
+    return SQLITE_OK;
+
+  // We acquired a shared lock that we can't get rid of.
+  sqlite_lock_mode_ = SQLITE_LOCK_SHARED;
+  return SQLITE_IOERR_CHECKRESERVEDLOCK;
+#endif  // defined(OS_FUCHSIA)
+}
+
+int SandboxedVfsFile::FileControl(int opcode, void* data) {
+  switch (opcode) {
+    case SQLITE_FCNTL_MMAP_SIZE:
+      // Implementing memory-mapping will require handling this correctly.
+      return SQLITE_NOTFOUND;
+    default:
+      return SQLITE_NOTFOUND;
+  }
+}
+
+int SandboxedVfsFile::SectorSize() {
+  return 0;
+}
+
+int SandboxedVfsFile::DeviceCharacteristics() {
+  // TODO(pwnall): Figure out if we can get away with returning 0 on Windows.
+#if defined(OS_WIN)
+  return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
+#else
+  // NOTE: SQLite's unix VFS attempts to detect the underlying filesystem and
+  // sets some flags based on the result.
+  return 0;
+#endif  // OS_WIN
+}
+
+int SandboxedVfsFile::ShmMap(int page_index,
+                             int page_size,
+                             int extend_file_if_needed,
+                             void volatile** result) {
+  DCHECK_GE(page_index, 0);
+  DCHECK_GE(page_size, 0);
+  DCHECK(result);
+
+  // NOTE: This would be needed by SQLite's WAL mode.
+  *result = nullptr;
+  return SQLITE_IOERR;
+}
+
+int SandboxedVfsFile::ShmLock(int offset, int size, int flags) {
+  DCHECK_GE(offset, 0);
+  DCHECK_GE(size, 0);
+
+  // NOTE: This would be needed by SQLite's WAL mode.
+  return SQLITE_IOERR;
+}
+
+void SandboxedVfsFile::ShmBarrier() {
+  // All writes to shared memory that have already been issued before this
+  // function is called must complete before the function returns.
+  std::atomic_thread_fence(std::memory_order_acq_rel);
+}
+
+int SandboxedVfsFile::ShmUnmap(int also_delete_file) {
+  // NOTE: This would be needed by SQLite's WAL mode.
+  return SQLITE_IOERR;
+}
+
+int SandboxedVfsFile::Fetch(sqlite3_int64 offset, int size, void** result) {
+  DCHECK_GE(offset, 0);
+  DCHECK_GE(size, 0);
+  DCHECK(result);
+
+  // NOTE: This would be needed for mmap()ed file support.
+  *result = nullptr;
+  return SQLITE_IOERR;
+}
+
+int SandboxedVfsFile::Unfetch(sqlite3_int64 offset, void* fetch_result) {
+  DCHECK_GE(offset, 0);
+  DCHECK(fetch_result);
+
+  // NOTE: This would be needed for mmap()ed file support.
+  return SQLITE_IOERR;
+}
+
+SandboxedVfsFile::SandboxedVfsFile(base::File file,
+                                   String file_name,
+                                   SandboxedVfs* vfs)
+    : file_(std::move(file)),
+      sqlite_lock_mode_(SQLITE_LOCK_NONE),
+      vfs_(vfs),
+      file_name_(std::move(file_name)) {}
+
+SandboxedVfsFile::~SandboxedVfsFile() = default;
+
+// static
+SandboxedVfsFileSqliteBridge* SandboxedVfsFileSqliteBridge::FromSqliteFile(
+    sqlite3_file* sqlite_file) {
+  static_assert(std::is_standard_layout<SandboxedVfsFileSqliteBridge>::value,
+                "needed for the reinterpret_cast below");
+  static_assert(offsetof(SandboxedVfsFileSqliteBridge, sqlite_file) == 0,
+                "sqlite_file must be the first member of the struct.");
+
+  SandboxedVfsFileSqliteBridge* bridge =
+      reinterpret_cast<SandboxedVfsFileSqliteBridge*>(sqlite_file);
+  DCHECK_EQ(sqlite_file, &bridge->sqlite_file)
+      << "assumed by the reinterpret_casts in the implementation";
+  return bridge;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs_file.h b/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs_file.h
new file mode 100644
index 0000000..a0e76ac
--- /dev/null
+++ b/third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs_file.h
@@ -0,0 +1,95 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBDATABASE_SQLITE_SANDBOXED_VFS_FILE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBDATABASE_SQLITE_SANDBOXED_VFS_FILE_H_
+
+#include "base/files/file.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/sqlite/sqlite3.h"
+
+namespace blink {
+
+class SandboxedVfs;
+class SandboxedVfsFile;
+
+// SQLite VFS file implementation that works in the Chrome renderer sandbox.
+//
+// An instance is created when SQLite calls into SandboxedVfs::Open(). The
+// instance is deleted by a call to SandboxedVfsFile::Close().
+//
+// The SQLite VFS API includes a complex locking strategy documented in
+// https://www.sqlite.org/lockingv3.html
+//
+// This implementation uses a simplified locking strategy, where we grab an
+// exclusive lock when entering any of the modes that prepare for a transition
+// to EXCLUSIVE. (These modes are RESERVED and PENDING). This approach is easy
+// to implement on top of base::File's locking primitives, at the cost of some
+// false contention, which makes us slower under high concurrency.
+//
+// SQLite's built-in VFSes use the OS support for locking a range of bytes in
+// the file, rather locking than the whole file.
+class SandboxedVfsFile {
+  USING_FAST_MALLOC(SandboxedVfsFile);
+
+ public:
+  // Creates an instance in the given buffer.
+  static void Create(base::File file,
+                     String file_name,
+                     SandboxedVfs* vfs,
+                     sqlite3_file* buffer);
+
+  // Extracts the instance bridged to the given SQLite VFS file.
+  static SandboxedVfsFile* FromSqliteFile(sqlite3_file* sqlite_file);
+
+  // sqlite3_file implementation.
+  int Close();
+  int Read(void* buffer, int size, sqlite3_int64 offset);
+  int Write(const void* buffer, int size, sqlite3_int64 offset);
+  int Truncate(sqlite3_int64 size);
+  int Sync(int flags);
+  int FileSize(sqlite3_int64* result_size);
+  int Lock(int mode);
+  int Unlock(int mode);
+  int CheckReservedLock(int* has_reserved_lock);
+  int FileControl(int opcode, void* data);
+  int SectorSize();
+  int DeviceCharacteristics();
+  int ShmMap(int page_index,
+             int page_size,
+             int extend_file_if_needed,
+             void volatile** result);
+  int ShmLock(int offset, int size, int flags);
+  void ShmBarrier();
+  int ShmUnmap(int also_delete_file);
+  int Fetch(sqlite3_int64 offset, int size, void** result);
+  int Unfetch(sqlite3_int64 offset, void* fetch_result);
+
+ private:
+  SandboxedVfsFile(base::File file, String file_name, SandboxedVfs* vfs);
+  ~SandboxedVfsFile();
+
+  // Constructed from a file handle passed from the browser process.
+  base::File file_;
+  // One of the SQLite locking mode constants.
+  int sqlite_lock_mode_;
+  // The SandboxedVfs that created this instance.
+  SandboxedVfs* const vfs_;
+  // Used to identify the file in IPCs to the browser process.
+  const String file_name_;
+};
+
+// sqlite3_file "subclass" that bridges to a SandboxedVfsFile instance.
+struct SandboxedVfsFileSqliteBridge {
+  sqlite3_file sqlite_file;
+  SandboxedVfsFile* blink_file;
+
+  static SandboxedVfsFileSqliteBridge* FromSqliteFile(
+      sqlite3_file* sqlite_file);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBDATABASE_SQLITE_SANDBOXED_VFS_FILE_H_
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_database.cc b/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_database.cc
index c442185a..674ef3a8 100644
--- a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_database.cc
+++ b/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_database.cc
@@ -27,8 +27,8 @@
 #include "third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_database.h"
 
 #include "third_party/blink/renderer/modules/webdatabase/database_authorizer.h"
+#include "third_party/blink/renderer/modules/webdatabase/sqlite/sandboxed_vfs.h"
 #include "third_party/blink/renderer/modules/webdatabase/sqlite/sql_log.h"
-#include "third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system.h"
 #include "third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_statement.h"
 #include "third_party/sqlite/sqlite3.h"
 
@@ -60,14 +60,15 @@
 bool SQLiteDatabase::Open(const String& filename) {
   Close();
 
-  open_error_ = SQLiteFileSystem::OpenDatabase(filename, &db_);
+  std::tie(open_error_, db_) =
+      SandboxedVfs::GetInstance().OpenDatabase(filename);
   if (open_error_ != SQLITE_OK) {
+    DCHECK_EQ(db_, nullptr);
+
     open_error_message_ =
         db_ ? sqlite3_errmsg(db_) : "sqlite_open returned null";
     DLOG(ERROR) << "SQLite database failed to load from " << filename
                 << "\nCause - " << open_error_message_.data();
-    sqlite3_close(db_);
-    db_ = nullptr;
     return false;
   }
 
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system.cc b/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system.cc
deleted file mode 100644
index ed1d1ab..0000000
--- a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2009 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/webdatabase/sqlite/sqlite_file_system.h"
-
-#include "sql/initialization.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
-#include "third_party/sqlite/sqlite3.h"
-
-// SQLiteFileSystem::registerSQLiteVFS() is implemented in the
-// platform-specific files SQLiteFileSystemChromium{Win|Posix}.cpp
-namespace blink {
-
-#if DCHECK_IS_ON()
-// static
-bool SQLiteFileSystem::initialize_sqlite_called_ = false;
-#endif  // DCHECK_IS_ON
-
-// static
-void SQLiteFileSystem::InitializeSQLite() {
-#if DCHECK_IS_ON()
-  DCHECK(!initialize_sqlite_called_) << __func__ << " already called";
-  initialize_sqlite_called_ = true;
-#endif  // DCHECK_IS_ON()
-
-  sql::EnsureSqliteInitialized();
-  RegisterSQLiteVFS();
-}
-
-// static
-int SQLiteFileSystem::OpenDatabase(const String& filename, sqlite3** database) {
-#if DCHECK_IS_ON()
-  DCHECK(initialize_sqlite_called_)
-      << "InitializeSQLite() must be called before " << __func__;
-#endif  // DCHECK_IS_ON()
-
-  return sqlite3_open_v2(
-      filename.Utf8().data(), database,
-      SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_PRIVATECACHE,
-      "chromium_vfs");
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system.h b/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system.h
deleted file mode 100644
index a4fc984..0000000
--- a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2009 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_WEBDATABASE_SQLITE_SQLITE_FILE_SYSTEM_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBDATABASE_SQLITE_SQLITE_FILE_SYSTEM_H_
-
-#include "third_party/blink/renderer/platform/wtf/allocator.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-#include "third_party/blink/renderer/platform/wtf/threading.h"
-
-struct sqlite3;
-
-namespace blink {
-
-// A class that abstracts the file system related operations required
-// by the WebKit database code.
-class SQLiteFileSystem {
-  STATIC_ONLY(SQLiteFileSystem);
-
- public:
-  // Initializes SQLite for Blink's use.
-  //
-  // This must be called exactly once in each renderer process that uses SQLite.
-  static void InitializeSQLite();
-
-  // Opens a database file.
-  //
-  // InitializeSQLite() must be called before this method is called.
-  //
-  // filename - The name of the database file.
-  // database - The SQLite structure that represents the database stored
-  //            in the given file.
-  static int OpenDatabase(const String& filename, sqlite3** database);
-
- private:
-  // Registers Chromium's VFS with SQLite.
-  static void RegisterSQLiteVFS();
-
-#if DCHECK_IS_ON()
-  static bool initialize_sqlite_called_;
-#endif  // DCHECK_IS_ON()
-};  // class SQLiteFileSystem
-
-}  // namespace blink
-
-#endif
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system_posix.cc b/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system_posix.cc
deleted file mode 100644
index 1ac378cd..0000000
--- a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system_posix.cc
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Copyright (C) 2009 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/webdatabase/sqlite/sqlite_file_system.h"
-
-#include "third_party/blink/public/platform/platform.h"
-#include "third_party/sqlite/sqlite3.h"
-
-#include <fcntl.h>
-#include <string.h>
-#include <unistd.h>
-
-namespace blink {
-
-// Chromium's Posix implementation of SQLite VFS
-namespace {
-
-struct chromiumVfsFile {
-  sqlite3_io_methods* p_methods;
-  sqlite3_file* wrapped_file;
-  char* wrapped_file_name;
-};
-
-int ChromiumClose(sqlite3_file* sqlite_file) {
-  chromiumVfsFile* chromium_file =
-      reinterpret_cast<chromiumVfsFile*>(sqlite_file);
-  int r = chromium_file->wrapped_file->pMethods->xClose(
-      chromium_file->wrapped_file);
-  sqlite3_free(chromium_file->wrapped_file_name);
-  sqlite3_free(chromium_file->wrapped_file);
-  memset(chromium_file, 0, sizeof(*chromium_file));
-  return r;
-}
-
-int ChromiumRead(sqlite3_file* sqlite_file,
-                 void* p_buf,
-                 int i_amt,
-                 sqlite3_int64 i_ofst) {
-  chromiumVfsFile* chromium_file =
-      reinterpret_cast<chromiumVfsFile*>(sqlite_file);
-  return chromium_file->wrapped_file->pMethods->xRead(
-      chromium_file->wrapped_file, p_buf, i_amt, i_ofst);
-}
-
-int ChromiumWrite(sqlite3_file* sqlite_file,
-                  const void* p_buf,
-                  int i_amt,
-                  sqlite3_int64 i_ofst) {
-  chromiumVfsFile* chromium_file =
-      reinterpret_cast<chromiumVfsFile*>(sqlite_file);
-  return chromium_file->wrapped_file->pMethods->xWrite(
-      chromium_file->wrapped_file, p_buf, i_amt, i_ofst);
-}
-
-int ChromiumTruncate(sqlite3_file* sqlite_file, sqlite3_int64 size) {
-  chromiumVfsFile* chromium_file =
-      reinterpret_cast<chromiumVfsFile*>(sqlite_file);
-
-  // The OSX and Linux sandboxes block ftruncate(), proxy to the browser
-  // process.
-  if (Platform::Current()->DatabaseSetFileSize(
-          String::FromUTF8(chromium_file->wrapped_file_name), size))
-    return SQLITE_OK;
-  return SQLITE_IOERR_TRUNCATE;
-}
-
-int ChromiumSync(sqlite3_file* sqlite_file, int flags) {
-  chromiumVfsFile* chromium_file =
-      reinterpret_cast<chromiumVfsFile*>(sqlite_file);
-  return chromium_file->wrapped_file->pMethods->xSync(
-      chromium_file->wrapped_file, flags);
-}
-
-int ChromiumFileSize(sqlite3_file* sqlite_file, sqlite3_int64* p_size) {
-  chromiumVfsFile* chromium_file =
-      reinterpret_cast<chromiumVfsFile*>(sqlite_file);
-  return chromium_file->wrapped_file->pMethods->xFileSize(
-      chromium_file->wrapped_file, p_size);
-}
-
-int ChromiumLock(sqlite3_file* sqlite_file, int e_file_lock) {
-  chromiumVfsFile* chromium_file =
-      reinterpret_cast<chromiumVfsFile*>(sqlite_file);
-  return chromium_file->wrapped_file->pMethods->xLock(
-      chromium_file->wrapped_file, e_file_lock);
-}
-
-int ChromiumUnlock(sqlite3_file* sqlite_file, int e_file_lock) {
-  chromiumVfsFile* chromium_file =
-      reinterpret_cast<chromiumVfsFile*>(sqlite_file);
-  return chromium_file->wrapped_file->pMethods->xUnlock(
-      chromium_file->wrapped_file, e_file_lock);
-}
-
-int ChromiumCheckReservedLock(sqlite3_file* sqlite_file, int* p_res_out) {
-  chromiumVfsFile* chromium_file =
-      reinterpret_cast<chromiumVfsFile*>(sqlite_file);
-  return chromium_file->wrapped_file->pMethods->xCheckReservedLock(
-      chromium_file->wrapped_file, p_res_out);
-}
-
-int ChromiumFileControl(sqlite3_file* sqlite_file, int op, void* p_arg) {
-  chromiumVfsFile* chromium_file =
-      reinterpret_cast<chromiumVfsFile*>(sqlite_file);
-  return chromium_file->wrapped_file->pMethods->xFileControl(
-      chromium_file->wrapped_file, op, p_arg);
-}
-
-int ChromiumSectorSize(sqlite3_file* sqlite_file) {
-  chromiumVfsFile* chromium_file =
-      reinterpret_cast<chromiumVfsFile*>(sqlite_file);
-  return chromium_file->wrapped_file->pMethods->xSectorSize(
-      chromium_file->wrapped_file);
-}
-
-int ChromiumDeviceCharacteristics(sqlite3_file* sqlite_file) {
-  chromiumVfsFile* chromium_file =
-      reinterpret_cast<chromiumVfsFile*>(sqlite_file);
-  return chromium_file->wrapped_file->pMethods->xDeviceCharacteristics(
-      chromium_file->wrapped_file);
-}
-
-// Opens a file.
-//
-// vfs - pointer to the sqlite3_vfs object.
-// fileName - the name of the file.
-// id - the structure that will manipulate the newly opened file.
-// desiredFlags - the desired open mode flags.
-// usedFlags - the actual open mode flags that were used.
-int ChromiumOpenInternal(sqlite3_vfs* vfs,
-                         const char* file_name,
-                         sqlite3_file* id,
-                         int desired_flags,
-                         int* used_flags) {
-  int fd = Platform::Current()->DatabaseOpenFile(String::FromUTF8(file_name),
-                                                 desired_flags);
-  if ((fd < 0) && (desired_flags & SQLITE_OPEN_READWRITE)) {
-    desired_flags =
-        (desired_flags & ~(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)) |
-        SQLITE_OPEN_READONLY;
-    fd = Platform::Current()->DatabaseOpenFile(String::FromUTF8(file_name),
-                                               desired_flags);
-  }
-  if (fd < 0)
-    return SQLITE_CANTOPEN;
-
-  if (used_flags)
-    *used_flags = desired_flags;
-
-  fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
-
-  // The mask 0x00007F00 gives us the 7 bits that determine the type of the file
-  // SQLite is trying to open.
-  int file_type = desired_flags & 0x00007F00;
-  int no_lock = (file_type != SQLITE_OPEN_MAIN_DB);
-  sqlite3_vfs* wrapped_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
-  return chromium_sqlite3_fill_in_unix_sqlite3_file(
-      wrapped_vfs, fd, id, file_name, no_lock, desired_flags);
-}
-
-int ChromiumOpen(sqlite3_vfs* vfs,
-                 const char* file_name,
-                 sqlite3_file* id,
-                 int desired_flags,
-                 int* used_flags) {
-  sqlite3_vfs* wrapped_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
-  sqlite3_file* wrapped_file =
-      static_cast<sqlite3_file*>(sqlite3_malloc(wrapped_vfs->szOsFile));
-  if (!wrapped_file)
-    return SQLITE_NOMEM;
-
-  // Make a local copy of the file name.  SQLite's os_unix.c appears to be
-  // written to allow caching the pointer passed in to this function, but that
-  // seems brittle.
-  char* wrapped_file_name = sqlite3_mprintf("%s", file_name);
-  if (!wrapped_file_name) {
-    sqlite3_free(wrapped_file);
-    return SQLITE_NOMEM;
-  }
-
-  // SQLite's unixOpen() makes assumptions about the structure of |fileName|.
-  // Our local copy may not answer those assumptions correctly.
-  int rc = ChromiumOpenInternal(vfs, file_name, wrapped_file, desired_flags,
-                                used_flags);
-  if (rc != SQLITE_OK) {
-    sqlite3_free(wrapped_file_name);
-    sqlite3_free(wrapped_file);
-    return rc;
-  }
-
-  static sqlite3_io_methods chromium_io_methods = {
-      1,
-      ChromiumClose,
-      ChromiumRead,
-      ChromiumWrite,
-      ChromiumTruncate,
-      ChromiumSync,
-      ChromiumFileSize,
-      ChromiumLock,
-      ChromiumUnlock,
-      ChromiumCheckReservedLock,
-      ChromiumFileControl,
-      ChromiumSectorSize,
-      ChromiumDeviceCharacteristics,
-      // Methods above are valid for version 1.
-  };
-  chromiumVfsFile* chromium_file = reinterpret_cast<chromiumVfsFile*>(id);
-  chromium_file->p_methods = &chromium_io_methods;
-  chromium_file->wrapped_file = wrapped_file;
-  chromium_file->wrapped_file_name = wrapped_file_name;
-  return SQLITE_OK;
-}
-
-// Deletes the given file.
-//
-// vfs - pointer to the sqlite3_vfs object.
-// fileName - the name of the file.
-// syncDir - determines if the directory to which this file belongs
-//           should be synched after the file is deleted.
-int ChromiumDelete(sqlite3_vfs*, const char* file_name, int sync_dir) {
-  return Platform::Current()->DatabaseDeleteFile(String::FromUTF8(file_name),
-                                                 sync_dir);
-}
-
-// Check the existence and status of the given file.
-//
-// vfs - pointer to the sqlite3_vfs object.
-// fileName - the name of the file.
-// flag - the type of test to make on this file.
-// res - the result.
-int ChromiumAccess(sqlite3_vfs*, const char* file_name, int flag, int* res) {
-  int attr = static_cast<int>(Platform::Current()->DatabaseGetFileAttributes(
-      String::FromUTF8(file_name)));
-  if (attr < 0) {
-    *res = 0;
-    return SQLITE_OK;
-  }
-
-  switch (flag) {
-    case SQLITE_ACCESS_EXISTS:
-      *res = 1;  // if the file doesn't exist, attr < 0
-      break;
-    case SQLITE_ACCESS_READWRITE:
-      *res = (attr & W_OK) && (attr & R_OK);
-      break;
-    case SQLITE_ACCESS_READ:
-      *res = (attr & R_OK);
-      break;
-    default:
-      return SQLITE_ERROR;
-  }
-
-  return SQLITE_OK;
-}
-
-// Turns a relative pathname into a full pathname.
-//
-// vfs - pointer to the sqlite3_vfs object.
-// relativePath - the relative path.
-// bufSize - the size of the output buffer in bytes.
-// absolutePath - the output buffer where the absolute path will be stored.
-int ChromiumFullPathname(sqlite3_vfs* vfs,
-                         const char* relative_path,
-                         int buf_size,
-                         char* absolute_path) {
-  // The renderer process doesn't need to know the absolute path of the file
-  sqlite3_snprintf(buf_size, absolute_path, "%s", relative_path);
-  return SQLITE_OK;
-}
-
-// Do not allow loading libraries in the renderer.
-void* ChromiumDlOpen(sqlite3_vfs*, const char*) {
-  return nullptr;
-}
-
-void ChromiumDlError(sqlite3_vfs*, int buf_size, char* error_buffer) {
-  sqlite3_snprintf(buf_size, error_buffer, "Dynamic loading not supported");
-}
-
-void (*ChromiumDlSym(sqlite3_vfs*, void*, const char*))(void) {
-  return nullptr;
-}
-
-void ChromiumDlClose(sqlite3_vfs*, void*) {}
-
-int ChromiumRandomness(sqlite3_vfs* vfs, int buf_size, char* buffer) {
-  sqlite3_vfs* wrapped_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
-  return wrapped_vfs->xRandomness(wrapped_vfs, buf_size, buffer);
-}
-
-int ChromiumSleep(sqlite3_vfs* vfs, int microseconds) {
-  sqlite3_vfs* wrapped_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
-  return wrapped_vfs->xSleep(wrapped_vfs, microseconds);
-}
-
-int ChromiumGetLastError(sqlite3_vfs* vfs, int n_buf, char* z_buf) {
-  if (n_buf && z_buf)
-    *z_buf = '\0';
-  return 0;
-}
-
-int ChromiumCurrentTimeInt64(sqlite3_vfs* vfs, sqlite3_int64* now) {
-  sqlite3_vfs* wrapped_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
-  return wrapped_vfs->xCurrentTimeInt64(wrapped_vfs, now);
-}
-
-}  // namespace
-
-void SQLiteFileSystem::RegisterSQLiteVFS() {
-  sqlite3_vfs* wrapped_vfs = sqlite3_vfs_find("unix");
-
-  // These are implemented by delegating to |wrappedVfs|.
-  // TODO(shess): Implement local versions.
-  DCHECK(wrapped_vfs->xRandomness);
-  DCHECK(wrapped_vfs->xSleep);
-  DCHECK(wrapped_vfs->xCurrentTimeInt64);
-
-  static sqlite3_vfs chromium_vfs = {2,  // SQLite VFS API version.
-                                     sizeof(chromiumVfsFile),
-                                     wrapped_vfs->mxPathname,
-                                     nullptr,
-                                     "chromium_vfs",
-                                     wrapped_vfs,
-                                     ChromiumOpen,
-                                     ChromiumDelete,
-                                     ChromiumAccess,
-                                     ChromiumFullPathname,
-                                     ChromiumDlOpen,
-                                     ChromiumDlError,
-                                     ChromiumDlSym,
-                                     ChromiumDlClose,
-                                     ChromiumRandomness,
-                                     ChromiumSleep,
-                                     nullptr,  // CurrentTime is deprecated.
-                                     ChromiumGetLastError,
-                                     ChromiumCurrentTimeInt64};
-  sqlite3_vfs_register(&chromium_vfs, 0);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system_win.cc b/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system_win.cc
deleted file mode 100644
index 5480922..0000000
--- a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system_win.cc
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2009 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/webdatabase/sqlite/sqlite_file_system.h"
-
-#include <windows.h>
-#include "third_party/blink/public/platform/platform.h"
-#include "third_party/sqlite/sqlite3.h"
-
-namespace blink {
-
-// Chromium's Windows implementation of SQLite VFS
-namespace {
-
-// Opens a file.
-//
-// vfs - pointer to the sqlite3_vfs object.
-// fileName - the name of the file.
-// id - the structure that will manipulate the newly opened file.
-// desiredFlags - the desired open mode flags.
-// usedFlags - the actual open mode flags that were used.
-int ChromiumOpen(sqlite3_vfs*,
-                 const char* file_name,
-                 sqlite3_file* id,
-                 int desired_flags,
-                 int* used_flags) {
-  HANDLE h = Platform::Current()->DatabaseOpenFile(String::FromUTF8(file_name),
-                                                   desired_flags);
-  if (h == INVALID_HANDLE_VALUE) {
-    if (desired_flags & SQLITE_OPEN_READWRITE) {
-      int new_flags =
-          (desired_flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE;
-      return ChromiumOpen(0, file_name, id, new_flags, used_flags);
-    } else
-      return SQLITE_CANTOPEN;
-  }
-  if (used_flags) {
-    if (desired_flags & SQLITE_OPEN_READWRITE)
-      *used_flags = SQLITE_OPEN_READWRITE;
-    else
-      *used_flags = SQLITE_OPEN_READONLY;
-  }
-
-  chromium_sqlite3_initialize_win_sqlite3_file(id, h);
-  return SQLITE_OK;
-}
-
-// Deletes the given file.
-//
-// vfs - pointer to the sqlite3_vfs object.
-// fileName - the name of the file.
-// syncDir - determines if the directory to which this file belongs
-//           should be synched after the file is deleted.
-int ChromiumDelete(sqlite3_vfs*, const char* file_name, int) {
-  return Platform::Current()->DatabaseDeleteFile(String::FromUTF8(file_name),
-                                                 false);
-}
-
-// Check the existence and status of the given file.
-//
-// vfs - pointer to the sqlite3_vfs object.
-// fileName - the name of the file.
-// flag - the type of test to make on this file.
-// res - the result.
-int ChromiumAccess(sqlite3_vfs*, const char* file_name, int flag, int* res) {
-  DWORD attr = Platform::Current()->DatabaseGetFileAttributes(
-      String::FromUTF8(file_name));
-  switch (flag) {
-    case SQLITE_ACCESS_READ:
-    case SQLITE_ACCESS_EXISTS:
-      *res = (attr != INVALID_FILE_ATTRIBUTES);
-      break;
-    case SQLITE_ACCESS_READWRITE:
-      *res = ((attr & FILE_ATTRIBUTE_READONLY) == 0);
-      break;
-    default:
-      return SQLITE_ERROR;
-  }
-
-  return SQLITE_OK;
-}
-
-// Turns a relative pathname into a full pathname.
-//
-// vfs - pointer to the sqlite3_vfs object.
-// relativePath - the relative path.
-// bufSize - the size of the output buffer in bytes.
-// absolutePath - the output buffer where the absolute path will be stored.
-int ChromiumFullPathname(sqlite3_vfs* vfs,
-                         const char* relative_path,
-                         int buf_size,
-                         char* absolute_path) {
-  // The renderer process doesn't need to know the absolute path of the file
-  sqlite3_snprintf(buf_size, absolute_path, "%s", relative_path);
-  return SQLITE_OK;
-}
-
-// Do not allow loading libraries in the renderer.
-void* ChromiumDlOpen(sqlite3_vfs*, const char*) {
-  return 0;
-}
-
-void ChromiumDlError(sqlite3_vfs*, int buf_size, char* error_buffer) {
-  sqlite3_snprintf(buf_size, error_buffer, "Dynamic loading not supported");
-}
-
-void (*ChromiumDlSym(sqlite3_vfs*, void*, const char*))(void) {
-  return 0;
-}
-
-void ChromiumDlClose(sqlite3_vfs*, void*) {}
-
-int ChromiumRandomness(sqlite3_vfs* vfs, int buf_size, char* buffer) {
-  sqlite3_vfs* wrapped_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
-  return wrapped_vfs->xRandomness(wrapped_vfs, buf_size, buffer);
-}
-
-int ChromiumSleep(sqlite3_vfs* vfs, int microseconds) {
-  sqlite3_vfs* wrapped_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
-  return wrapped_vfs->xSleep(wrapped_vfs, microseconds);
-}
-
-int ChromiumGetLastError(sqlite3_vfs* vfs, int n_buf, char* z_buf) {
-  if (n_buf && z_buf)
-    *z_buf = '\0';
-  return 0;
-}
-
-int ChromiumCurrentTimeInt64(sqlite3_vfs* vfs, sqlite3_int64* now) {
-  sqlite3_vfs* wrapped_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
-  return wrapped_vfs->xCurrentTimeInt64(wrapped_vfs, now);
-}
-
-}  // namespace
-
-void SQLiteFileSystem::RegisterSQLiteVFS() {
-  sqlite3_vfs* wrapped_vfs = sqlite3_vfs_find("win32");
-
-  // These are implemented by delegating to |wrappedVfs|.
-  // TODO(shess): Implement local versions.
-  DCHECK(wrapped_vfs->xRandomness);
-  DCHECK(wrapped_vfs->xSleep);
-  DCHECK(wrapped_vfs->xCurrentTimeInt64);
-
-  static sqlite3_vfs chromium_vfs = {2,  // SQLite VFS API version.
-                                     wrapped_vfs->szOsFile,
-                                     wrapped_vfs->mxPathname,
-                                     0,
-                                     "chromium_vfs",
-                                     wrapped_vfs,
-                                     ChromiumOpen,
-                                     ChromiumDelete,
-                                     ChromiumAccess,
-                                     ChromiumFullPathname,
-                                     ChromiumDlOpen,
-                                     ChromiumDlError,
-                                     ChromiumDlSym,
-                                     ChromiumDlClose,
-                                     ChromiumRandomness,
-                                     ChromiumSleep,
-                                     nullptr,  // CurrentTime is deprecated.
-                                     ChromiumGetLastError,
-                                     ChromiumCurrentTimeInt64};
-  sqlite3_vfs_register(&chromium_vfs, 0);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/BUILD.gn b/third_party/blink/renderer/modules/webgpu/BUILD.gn
index 55b0ec0..bc0d50b 100644
--- a/third_party/blink/renderer/modules/webgpu/BUILD.gn
+++ b/third_party/blink/renderer/modules/webgpu/BUILD.gn
@@ -16,6 +16,8 @@
     "gpu_adapter.h",
     "gpu_device.cc",
     "gpu_device.h",
+    "gpu_queue.cc",
+    "gpu_queue.h",
     "navigator_gpu.cc",
     "navigator_gpu.h",
   ]
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.cc b/third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.cc
index 19ce0b7..dd55a38 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.cc
+++ b/third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h"
 
 #include "base/logging.h"
+#include "gpu/command_buffer/client/webgpu_interface.h"
 
 namespace blink {
 
@@ -25,4 +26,10 @@
   return interface_;
 }
 
+const DawnProcTable& DawnControlClientHolder::GetProcs() const {
+  DCHECK(!destroyed_);
+  DCHECK(interface_);
+  return interface_->GetProcs();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h b/third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h
index 61db6e9..52093ff8 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h
+++ b/third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_CONTROL_CLIENT_HOLDER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_CONTROL_CLIENT_HOLDER_H_
 
+#include <dawn/dawn.h>
+
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 
 namespace gpu {
@@ -28,6 +30,7 @@
   bool IsDestroyed() const;
 
   gpu::webgpu::WebGPUInterface* GetInterface() const;
+  const DawnProcTable& GetProcs() const;
 
  private:
   friend class RefCounted<DawnControlClientHolder>;
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_object.cc b/third_party/blink/renderer/modules/webgpu/dawn_object.cc
index 7886a13b..a3bd513 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_object.cc
+++ b/third_party/blink/renderer/modules/webgpu/dawn_object.cc
@@ -5,26 +5,39 @@
 #include "third_party/blink/renderer/modules/webgpu/dawn_object.h"
 
 #include "third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
 
 namespace blink {
 
-DawnObject::DawnObject(
+DawnObjectBase::DawnObjectBase(
     scoped_refptr<DawnControlClientHolder> dawn_control_client)
-    : dawn_control_client_(dawn_control_client) {}
+    : dawn_control_client_(std::move(dawn_control_client)) {}
 
-DawnObject::~DawnObject() = default;
+DawnObjectBase::~DawnObjectBase() = default;
 
-const scoped_refptr<DawnControlClientHolder>& DawnObject::GetDawnControlClient()
-    const {
+const scoped_refptr<DawnControlClientHolder>&
+DawnObjectBase::GetDawnControlClient() const {
   return dawn_control_client_;
 }
 
-bool DawnObject::IsDawnControlClientDestroyed() const {
+bool DawnObjectBase::IsDawnControlClientDestroyed() const {
   return dawn_control_client_->IsDestroyed();
 }
 
-gpu::webgpu::WebGPUInterface* DawnObject::GetInterface() const {
+gpu::webgpu::WebGPUInterface* DawnObjectBase::GetInterface() const {
   return dawn_control_client_->GetInterface();
 }
 
+const DawnProcTable& DawnObjectBase::GetProcs() const {
+  return dawn_control_client_->GetProcs();
+}
+
+DawnObjectImpl::DawnObjectImpl(GPUDevice* device)
+    : DawnObjectBase(device->GetDawnControlClient()), device_(device) {}
+
+void DawnObjectImpl::Trace(blink::Visitor* visitor) {
+  visitor->Trace(device_);
+  ScriptWrappable::Trace(visitor);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_object.h b/third_party/blink/renderer/modules/webgpu/dawn_object.h
index d076646..8dbec14 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_object.h
+++ b/third_party/blink/renderer/modules/webgpu/dawn_object.h
@@ -5,7 +5,10 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_OBJECT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_OBJECT_H_
 
+#include <dawn/dawn.h>
+
 #include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 namespace gpu {
@@ -18,25 +21,62 @@
 
 namespace blink {
 
-class DawnControlClientHolder;
+class GPUDevice;
+class Visitor;
 
 // This class allows objects to hold onto a DawnControlClientHolder.
 // The DawnControlClientHolder is used to hold the WebGPUInterface and keep
 // track of whether or not the client has been destroyed. If the client is
 // destroyed, we should not call any Dawn functions.
-class DawnObject : public ScriptWrappable {
+class DawnObjectBase : public ScriptWrappable {
  public:
-  DawnObject(scoped_refptr<DawnControlClientHolder> dawn_control_client);
-  ~DawnObject() override;
+  DawnObjectBase(scoped_refptr<DawnControlClientHolder> dawn_control_client);
+  ~DawnObjectBase() override;
 
   const scoped_refptr<DawnControlClientHolder>& GetDawnControlClient() const;
   bool IsDawnControlClientDestroyed() const;
   gpu::webgpu::WebGPUInterface* GetInterface() const;
+  const DawnProcTable& GetProcs() const;
 
  private:
   scoped_refptr<DawnControlClientHolder> dawn_control_client_;
 };
 
+class DawnObjectImpl : public DawnObjectBase {
+ public:
+  DawnObjectImpl(GPUDevice* device);
+
+  void Trace(blink::Visitor* visitor) override;
+
+ protected:
+  Member<GPUDevice> device_;
+};
+
+template <typename Handle>
+class DawnObject : public DawnObjectImpl {
+ public:
+  DawnObject(GPUDevice* device, Handle handle)
+      : DawnObjectImpl(device), handle_(handle) {}
+
+  Handle GetHandle() const { return handle_; }
+
+ private:
+  Handle const handle_;
+};
+
+template <>
+class DawnObject<DawnDevice> : public DawnObjectBase {
+ public:
+  DawnObject(scoped_refptr<DawnControlClientHolder> dawn_control_client,
+             DawnDevice handle)
+      : DawnObjectBase(std::move(dawn_control_client)), handle_(handle) {}
+
+  DawnDevice GetHandle() const { return handle_; }
+
+ private:
+  DawnDevice const handle_;
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_OBJECT_H_
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
index 75d46c4..48b33fa 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
@@ -19,7 +19,7 @@
 GPUAdapter::GPUAdapter(
     const String& name,
     scoped_refptr<DawnControlClientHolder> dawn_control_client)
-    : DawnObject(std::move(dawn_control_client)), name_(name) {}
+    : DawnObjectBase(std::move(dawn_control_client)), name_(name) {}
 
 const String& GPUAdapter::name() const {
   return name_;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.h b/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
index 0b93553..8302580e 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
@@ -15,7 +15,7 @@
 class GPUDevice;
 class GPUDeviceDescriptor;
 
-class GPUAdapter final : public DawnObject {
+class GPUAdapter final : public DawnObjectBase {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.cc b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
index a43571e..6191f10a 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
@@ -4,10 +4,12 @@
 
 #include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
 
+#include "gpu/command_buffer/client/webgpu_interface.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/modules/webgpu/dawn_control_client_holder.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_adapter.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_device_descriptor.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_queue.h"
 
 namespace blink {
 
@@ -24,15 +26,31 @@
 GPUDevice::GPUDevice(scoped_refptr<DawnControlClientHolder> dawn_control_client,
                      GPUAdapter* adapter,
                      const GPUDeviceDescriptor* descriptor)
-    : DawnObject(std::move(dawn_control_client)), adapter_(adapter) {}
+    : DawnObject(dawn_control_client,
+                 dawn_control_client->GetInterface()->GetDefaultDevice()),
+      adapter_(adapter),
+      queue_(
+          GPUQueue::Create(this, GetProcs().deviceCreateQueue(GetHandle()))) {}
+
+GPUDevice::~GPUDevice() {
+  if (IsDawnControlClientDestroyed()) {
+    return;
+  }
+  GetProcs().deviceRelease(GetHandle());
+}
 
 GPUAdapter* GPUDevice::adapter() const {
   return adapter_;
 }
 
+GPUQueue* GPUDevice::getQueue() {
+  return queue_;
+}
+
 void GPUDevice::Trace(blink::Visitor* visitor) {
   visitor->Trace(adapter_);
-  ScriptWrappable::Trace(visitor);
+  visitor->Trace(queue_);
+  DawnObject<DawnDevice>::Trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.h b/third_party/blink/renderer/modules/webgpu/gpu_device.h
index 2f415061..45c95c0d 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.h
@@ -12,8 +12,9 @@
 
 class GPUAdapter;
 class GPUDeviceDescriptor;
+class GPUQueue;
 
-class GPUDevice final : public DawnObject {
+class GPUDevice final : public DawnObject<DawnDevice> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -24,14 +25,17 @@
   explicit GPUDevice(scoped_refptr<DawnControlClientHolder> dawn_control_client,
                      GPUAdapter* adapter,
                      const GPUDeviceDescriptor* descriptor);
+  ~GPUDevice() override;
 
   void Trace(blink::Visitor* visitor) override;
 
   // gpu_device.idl
   GPUAdapter* adapter() const;
+  GPUQueue* getQueue();
 
  private:
   Member<GPUAdapter> adapter_;
+  Member<GPUQueue> queue_;
 
   DISALLOW_COPY_AND_ASSIGN(GPUDevice);
 };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.idl b/third_party/blink/renderer/modules/webgpu/gpu_device.idl
index 3a1dc5d..23bf016 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.idl
@@ -8,4 +8,6 @@
     RuntimeEnabled=WebGPU
 ] interface GPUDevice {
     readonly attribute GPUAdapter adapter;
+
+    GPUQueue getQueue();
 };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
new file mode 100644
index 0000000..71edc44a
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
@@ -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.
+
+#include "third_party/blink/renderer/modules/webgpu/gpu_queue.h"
+
+#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
+
+namespace blink {
+
+// static
+GPUQueue* GPUQueue::Create(GPUDevice* device, DawnQueue queue) {
+  return MakeGarbageCollected<GPUQueue>(device, queue);
+}
+
+GPUQueue::GPUQueue(GPUDevice* device, DawnQueue queue)
+    : DawnObject<DawnQueue>(device, queue) {}
+
+GPUQueue::~GPUQueue() {
+  if (IsDawnControlClientDestroyed()) {
+    return;
+  }
+  GetProcs().queueRelease(GetHandle());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_queue.h b/third_party/blink/renderer/modules/webgpu/gpu_queue.h
new file mode 100644
index 0000000..d8d7be6
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgpu/gpu_queue.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_QUEUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_QUEUE_H_
+
+#include "third_party/blink/renderer/modules/webgpu/dawn_object.h"
+
+namespace blink {
+
+class GPUQueue : public DawnObject<DawnQueue> {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static GPUQueue* Create(GPUDevice* device, DawnQueue queue);
+  explicit GPUQueue(GPUDevice* device, DawnQueue queue);
+  ~GPUQueue() override;
+  
+  DISALLOW_COPY_AND_ASSIGN(GPUQueue);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_QUEUE_H_
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_queue.idl b/third_party/blink/renderer/modules/webgpu/gpu_queue.idl
new file mode 100644
index 0000000..abf1c20
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgpu/gpu_queue.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://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
+
+[
+    RuntimeEnabled=WebGPU
+] interface GPUQueue {
+};
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc
index 2c1d471..9c3c0223 100644
--- a/third_party/blink/renderer/modules/xr/xr.cc
+++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -272,11 +272,7 @@
 void XR::DispatchRequestSession(PendingSessionQuery* query) {
   if (!device_) {
     if (query->mode == XRSession::kModeInline) {
-      XRSession* session = MakeGarbageCollected<XRSession>(
-          this, nullptr /* client request */, query->mode,
-          XRSession::kBlendModeOpaque);
-      sessions_.insert(session);
-      query->resolver->Resolve(session);
+      CreateInlineIdentitySession(query);
       return;
     }
 
@@ -377,6 +373,11 @@
   // TODO(https://crbug.com/872316) Improve the error messaging to indicate why
   // a request failed.
   if (!session_ptr) {
+    if (query->mode == XRSession::kModeInline) {
+      CreateInlineIdentitySession(query);
+      return;
+    }
+
     DOMException* exception = DOMException::Create(
         DOMExceptionCode::kNotSupportedError, kSessionNotSupported);
     query->resolver->Reject(exception);
@@ -458,6 +459,14 @@
   Dispose();
 }
 
+void XR::CreateInlineIdentitySession(PendingSessionQuery* query) {
+  XRSession* session =
+      MakeGarbageCollected<XRSession>(this, nullptr /* client request */,
+                                      query->mode, XRSession::kBlendModeOpaque);
+  sessions_.insert(session);
+  query->resolver->Resolve(session);
+}
+
 void XR::Dispose() {
   // If the document context was destroyed, shut down the client connection
   // and never call the mojo service again.
diff --git a/third_party/blink/renderer/modules/xr/xr.h b/third_party/blink/renderer/modules/xr/xr.h
index 37b79b8..40d1c236 100644
--- a/third_party/blink/renderer/modules/xr/xr.h
+++ b/third_party/blink/renderer/modules/xr/xr.h
@@ -107,6 +107,8 @@
   void AddedEventListener(const AtomicString& event_type,
                           RegisteredEventListener&) override;
 
+  void CreateInlineIdentitySession(PendingSessionQuery*);
+
   void Dispose();
 
   void OnEnvironmentProviderDisconnect();
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 24a15b9..b1e21cf 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -213,6 +213,7 @@
   TRACE_EVENT0("gpu", __FUNCTION__);
   DCHECK(!immersive_session_)
       << "Scheduling should be done via the exclusive session if present.";
+  DCHECK(xr_->xrMagicWindowProviderPtr() || !HasARSession());
 
   if (pending_non_immersive_vsync_)
     return;
@@ -227,13 +228,21 @@
   if (!doc)
     return;
 
+  // This is cleared by either OnNonImmersiveFrameData (GetFrameData callback)
+  // or by OnNonImmersiveVSync (XRFrameProviderRequestCallback's invoke)
+  // Currently the only way for neither of these methods to be called is
+  // if we don't have a MagicWindowProvider and we have an AR Session
+  // which is guaranteed by our above DCheck.
   pending_non_immersive_vsync_ = true;
 
+  // If we have a Magic Window provider, request frame data and flag that
+  // we're waiting for it.  If not, clear any pose data, so that
+  // ProcessScheduledFrame handles it appropriately.
   if (xr_->xrMagicWindowProviderPtr()) {
     xr_->xrMagicWindowProviderPtr()->GetFrameData(WTF::Bind(
         &XRFrameProvider::OnNonImmersiveFrameData, WrapWeakPersistent(this)));
   } else {
-    OnNonImmersiveFrameData(nullptr);
+    frame_pose_ = nullptr;
   }
 
   // TODO(https://crbug.com/839253): Generalize the pass-through images
@@ -333,14 +342,9 @@
     return;
 
   if (!frame_data) {
-    // Since we don't have any frame data, we will try to request a regular
-    // animation frame to avoid getting stuck.
-    // If we have a MagicWindowProvider this is unexpected, and we should log
-    // for diagnostic purposes.
-    if (xr_->xrMagicWindowProviderPtr()) {
-      DVLOG(1) << __FUNCTION__ << ": NO FRAME DATA!";
-    }
-
+    // Unexpectedly didn't get frame data, and we don't have a timestamp.
+    // Try to request a regular animation frame to avoid getting stuck.
+    DVLOG(1) << __FUNCTION__ << ": NO FRAME DATA!";
     frame_pose_ = nullptr;
     doc->RequestAnimationFrame(
         MakeGarbageCollected<XRFrameProviderRequestCallback>(this));
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h
index d9db82d..78bb70d 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.h
+++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -155,7 +155,13 @@
 
   // TODO(jacde): Update the mojom to deliver this per-frame.
   bool EmulatedPosition() const {
-    return !display_info_->capabilities->hasPosition;
+    if (display_info_) {
+      return !display_info_->capabilities->hasPosition;
+    }
+
+    // If we don't have display info then we should be using the identity
+    // reference space, which by definition will be emulating the position.
+    return true;
   }
 
   void UpdateDisplayInfo(
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string.cc b/third_party/blink/renderer/platform/bindings/parkable_string.cc
index e1c38bd..7136c792 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string.cc
+++ b/third_party/blink/renderer/platform/bindings/parkable_string.cc
@@ -519,7 +519,8 @@
 
 void ParkableStringImpl::OnParkingCompleteOnMainThread(
     std::unique_ptr<CompressionTaskParams> params,
-    std::unique_ptr<Vector<uint8_t>> compressed) {
+    std::unique_ptr<Vector<uint8_t>> compressed,
+    base::TimeDelta parking_thread_time) {
   MutexLocker locker(mutex_);
   DCHECK_EQ(State::kParkingInProgress, state_);
 
@@ -547,6 +548,10 @@
   } else {
     state_ = State::kUnparked;
   }
+  // Record the time no matter whether the string was parked or not, as the
+  // parking cost was paid.
+  ParkableStringManager::Instance().RecordParkingThreadTime(
+      parking_thread_time);
 }
 
 // static
@@ -573,6 +578,11 @@
                          params->size);
   std::unique_ptr<Vector<uint8_t>> compressed = nullptr;
 
+  // This runs in background, making CPU starvation likely, and not an issue.
+  // Hence, report thread time instead of wall clock time.
+  bool thread_ticks_supported = base::ThreadTicks::IsSupported();
+  auto tick =
+      thread_ticks_supported ? base::ThreadTicks::Now() : base::ThreadTicks();
   {
     // Temporary vector. As we don't want to waste memory, the temporary buffer
     // has the same size as the initial data. Compression will fail if this is
@@ -610,6 +620,8 @@
                          compressed_size);
     }
   }
+  auto tock =
+      thread_ticks_supported ? base::ThreadTicks::Now() : base::ThreadTicks();
 
   auto* task_runner = params->callback_task_runner.get();
   size_t size = params->size;
@@ -617,12 +629,14 @@
       *task_runner, FROM_HERE,
       CrossThreadBind(
           [](std::unique_ptr<CompressionTaskParams> params,
-             std::unique_ptr<Vector<uint8_t>> compressed) {
+             std::unique_ptr<Vector<uint8_t>> compressed,
+             base::TimeDelta parking_thread_time) {
             auto* string = params->string.get();
-            string->OnParkingCompleteOnMainThread(std::move(params),
-                                                  std::move(compressed));
+            string->OnParkingCompleteOnMainThread(
+                std::move(params), std::move(compressed), parking_thread_time);
           },
-          WTF::Passed(std::move(params)), WTF::Passed(std::move(compressed))));
+          WTF::Passed(std::move(params)), WTF::Passed(std::move(compressed)),
+          tock - tick));
   RecordStatistics(size, timer.Elapsed(), ParkingAction::kParkedInBackground);
 }
 
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string.h b/third_party/blink/renderer/platform/bindings/parkable_string.h
index 05f8e2b..ede2d5f 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string.h
+++ b/third_party/blink/renderer/platform/bindings/parkable_string.h
@@ -151,9 +151,12 @@
   // Called on the main thread after compression is done.
   // |params| is the same as the one passed to |CompressInBackground()|,
   // |compressed| is the compressed data, nullptr if compression failed.
+  // |parking_thread_time| is the CPU time used by the background compression
+  // task.
   void OnParkingCompleteOnMainThread(
       std::unique_ptr<CompressionTaskParams> params,
-      std::unique_ptr<Vector<uint8_t>> compressed);
+      std::unique_ptr<Vector<uint8_t>> compressed,
+      base::TimeDelta parking_thread_time);
 
   // Background thread.
   static void CompressInBackground(std::unique_ptr<CompressionTaskParams>);
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc b/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc
index 846499a..d88b2d4 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc
+++ b/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc
@@ -399,6 +399,10 @@
 void ParkableStringManager::RecordStatisticsAfter5Minutes() const {
   base::UmaHistogramTimes("Memory.ParkableString.MainThreadTime.5min",
                           total_unparking_time_);
+  if (base::ThreadTicks::IsSupported()) {
+    base::UmaHistogramTimes("Memory.ParkableString.ParkingThreadTime.5min",
+                            total_parking_thread_time_);
+  }
   Statistics stats = ComputeStatistics();
   RecordMemoryStatistics(stats, ".5min");
 }
@@ -515,6 +519,7 @@
   has_posted_unparking_time_accounting_task_ = false;
   did_register_memory_pressure_listener_ = false;
   total_unparking_time_ = base::TimeDelta();
+  total_parking_thread_time_ = base::TimeDelta();
   unparked_strings_.clear();
   parked_strings_.clear();
 }
@@ -527,6 +532,7 @@
       has_posted_unparking_time_accounting_task_(false),
       did_register_memory_pressure_listener_(false),
       total_unparking_time_(),
+      total_parking_thread_time_(),
       unparked_strings_(),
       parked_strings_() {}
 
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string_manager.h b/third_party/blink/renderer/platform/bindings/parkable_string_manager.h
index 6191c39..af3b4a0f 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string_manager.h
+++ b/third_party/blink/renderer/platform/bindings/parkable_string_manager.h
@@ -91,6 +91,9 @@
   void AgeStringsAndPark();
   void ScheduleAgingTaskIfNeeded();
   void RecordUnparkingTime(base::TimeDelta);
+  void RecordParkingThreadTime(base::TimeDelta parking_thread_time) {
+    total_parking_thread_time_ += parking_thread_time;
+  }
   Vector<ParkableStringImpl*> GetUnparkedStrings() const;
   Statistics ComputeStatistics() const;
 
@@ -105,7 +108,7 @@
   bool has_posted_unparking_time_accounting_task_;
   bool did_register_memory_pressure_listener_;
   base::TimeDelta total_unparking_time_;
-
+  base::TimeDelta total_parking_thread_time_;
   WTF::HashSet<ParkableStringImpl*, ParkableStringImplHash> unparked_strings_;
   WTF::HashSet<ParkableStringImpl*, ParkableStringImplHash> parked_strings_;
 
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 9ab6b3f..03c5482 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
+++ b/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
@@ -984,6 +984,13 @@
   histogram_tester.ExpectBucketCount(
       "Memory.ParkableString.MainThreadTime.5min", 0, 0);
 
+  if (base::ThreadTicks::IsSupported()) {
+    histogram_tester.ExpectTotalCount(
+        "Memory.ParkableString.ParkingThreadTime.5min", 1);
+    histogram_tester.ExpectBucketCount(
+        "Memory.ParkableString.ParkingThreadTime.5min", 0, 0);
+  }
+
   histogram_tester.ExpectUniqueSample("Memory.ParkableString.TotalSizeKb.5min",
                                       original_size / 1000, 1);
   histogram_tester.ExpectUniqueSample(
diff --git a/third_party/blink/renderer/platform/fonts/simple_font_data.cc b/third_party/blink/renderer/platform/fonts/simple_font_data.cc
index 144c227..f15c132 100644
--- a/third_party/blink/renderer/platform/fonts/simple_font_data.cc
+++ b/third_party/blink/renderer/platform/fonts/simple_font_data.cc
@@ -205,7 +205,7 @@
 scoped_refptr<SimpleFontData> SimpleFontData::SmallCapsFontData(
     const FontDescription& font_description) const {
   if (!derived_font_data_)
-    derived_font_data_ = DerivedFontData::Create();
+    derived_font_data_ = std::make_unique<DerivedFontData>();
   if (!derived_font_data_->small_caps)
     derived_font_data_->small_caps =
         CreateScaledFontData(font_description, kSmallCapsFontSizeMultiplier);
@@ -216,7 +216,7 @@
 scoped_refptr<SimpleFontData> SimpleFontData::EmphasisMarkFontData(
     const FontDescription& font_description) const {
   if (!derived_font_data_)
-    derived_font_data_ = DerivedFontData::Create();
+    derived_font_data_ = std::make_unique<DerivedFontData>();
   if (!derived_font_data_->emphasis_mark)
     derived_font_data_->emphasis_mark =
         CreateScaledFontData(font_description, kEmphasisMarkFontSizeMultiplier);
@@ -224,11 +224,6 @@
   return derived_font_data_->emphasis_mark;
 }
 
-std::unique_ptr<SimpleFontData::DerivedFontData>
-SimpleFontData::DerivedFontData::Create() {
-  return base::WrapUnique(new DerivedFontData);
-}
-
 scoped_refptr<SimpleFontData> SimpleFontData::CreateScaledFontData(
     const FontDescription& font_description,
     float scale_factor) const {
diff --git a/third_party/blink/renderer/platform/fonts/simple_font_data.h b/third_party/blink/renderer/platform/fonts/simple_font_data.h
index 7cf3793..4440450 100644
--- a/third_party/blink/renderer/platform/fonts/simple_font_data.h
+++ b/third_party/blink/renderer/platform/fonts/simple_font_data.h
@@ -180,14 +180,11 @@
     USING_FAST_MALLOC(DerivedFontData);
 
    public:
-    static std::unique_ptr<DerivedFontData> Create();
+    DerivedFontData() = default;
 
     scoped_refptr<SimpleFontData> small_caps;
     scoped_refptr<SimpleFontData> emphasis_mark;
 
-   private:
-    DerivedFontData() = default;
-
     DISALLOW_COPY_AND_ASSIGN(DerivedFontData);
   };
 
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc b/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
index a71d0fb..16680772 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
@@ -605,7 +605,7 @@
   void SetUp() override {
     BitmapImageTest::SetUp();
 
-    auto decoder = MockImageDecoder::Create(this);
+    auto decoder = std::make_unique<MockImageDecoder>(this);
     decoder->SetSize(10, 10);
     image_->SetDecoderForTesting(
         DeferredImageDecoder::CreateForTesting(std::move(decoder)));
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 3e55322..1c27aba 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
@@ -641,8 +641,8 @@
     if (IsAccelerated() && !rate_limiter_) {
       // Make sure the GPU is never more than two animation frames behind.
       constexpr unsigned kMaxCanvasAnimationBacklog = 2;
-      rate_limiter_ =
-          SharedContextRateLimiter::Create(kMaxCanvasAnimationBacklog);
+      rate_limiter_ = std::make_unique<SharedContextRateLimiter>(
+          kMaxCanvasAnimationBacklog);
     }
   }
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index 5b39453..cd76d1c1 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -35,15 +35,6 @@
 
 namespace {
 
-// Returns true if the given element namespace is one of the ones needed for
-// running animations on the compositor. These are the only element_ids the
-// compositor needs to track existence of in the element id set.
-bool IsAnimationNamespace(CompositorElementIdNamespace element_namespace) {
-  return element_namespace == CompositorElementIdNamespace::kPrimaryTransform ||
-         element_namespace == CompositorElementIdNamespace::kPrimaryEffect ||
-         element_namespace == CompositorElementIdNamespace::kEffectFilter;
-}
-
 // Inserts the element ids of the given node and all of its ancestors into the
 // given |composited_element_ids| set. Filters out specifically element ids
 // which are needed for animations. Returns once it finds an id which already
@@ -55,8 +46,7 @@
     CompositorElementIdSet& composited_element_ids) {
   for (const auto* n = &node; n; n = SafeUnalias(n->Parent())) {
     const CompositorElementId& element_id = n->GetCompositorElementId();
-    if (element_id &&
-        IsAnimationNamespace(NamespaceFromCompositorElementId(element_id))) {
+    if (element_id && n->RequiresCompositingForAnimation()) {
       if (composited_element_ids.count(element_id)) {
         // Once we reach a node already counted we can stop traversing the
         // parent chain.
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
index 4198db41..2bbbcc7 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
@@ -60,14 +60,10 @@
   USING_FAST_MALLOC(PaintArtifactCompositor);
 
  public:
-  ~PaintArtifactCompositor();
-
-  static std::unique_ptr<PaintArtifactCompositor> Create(
+  PaintArtifactCompositor(
       base::RepeatingCallback<void(const gfx::ScrollOffset&,
-                                   const cc::ElementId&)> scroll_callback) {
-    return base::WrapUnique(
-        new PaintArtifactCompositor(std::move(scroll_callback)));
-  }
+                                   const cc::ElementId&)> scroll_callback);
+  ~PaintArtifactCompositor();
 
   struct ViewportProperties {
     const TransformPaintPropertyNode* page_scale = nullptr;
@@ -165,10 +161,6 @@
     bool requires_own_layer;
   };
 
-  PaintArtifactCompositor(
-      base::RepeatingCallback<void(const gfx::ScrollOffset&,
-                                   const cc::ElementId&)> scroll_callback);
-
   // Collects the PaintChunks into groups which will end up in the same
   // cc layer. This is the entry point of the layerization algorithm.
   void CollectPendingLayers(const PaintArtifact&,
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index 0b0b798c..8a4668b1 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -89,7 +89,7 @@
   void SetUp() override {
     // Delay constructing the compositor until after the feature is set.
     paint_artifact_compositor_ =
-        PaintArtifactCompositor::Create(WTF::BindRepeating(
+        std::make_unique<PaintArtifactCompositor>(WTF::BindRepeating(
             &FakeScrollClient::DidScroll, WTF::Unretained(&scroll_client_)));
     paint_artifact_compositor_->EnableExtraDataForTesting();
 
@@ -933,14 +933,14 @@
 }
 
 TEST_P(PaintArtifactCompositorTest, EffectTreeConversionWithAlias) {
-  EffectPaintPropertyNode::State effect1_state;
-  effect1_state.local_transform_space = &t0();
-  effect1_state.output_clip = &c0();
-  effect1_state.opacity = 0.5;
-  effect1_state.direct_compositing_reasons = CompositingReason::kAll;
-  effect1_state.compositor_element_id = CompositorElementId(2);
+  Update(TestPaintArtifact()
+             .Chunk()
+             .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite)
+             .Build());
+  auto root_stable_id = GetPropertyTrees().effect_tree.Node(1)->stable_id;
+
   auto real_effect1 =
-      EffectPaintPropertyNode::Create(e0(), std::move(effect1_state));
+      CreateOpacityEffect(e0(), t0(), &c0(), 0.5, CompositingReason::kAll);
   auto effect1 = EffectPaintPropertyNode::CreateAlias(*real_effect1);
   auto real_effect2 =
       CreateOpacityEffect(*effect1, 0.3, CompositingReason::kAll);
@@ -960,20 +960,19 @@
   ASSERT_EQ(3u, ContentLayerCount());
 
   const cc::EffectTree& effect_tree = GetPropertyTrees().effect_tree;
-  // Node #0 reserved for null; #1 for root render surface; #2 for
-  // e0(), plus 3 nodes for those created by
-  // this test.
+  // Node #0 reserved for null; #1 for root render surface; #2 for e0(),
+  // plus 3 nodes for those created by this test.
   ASSERT_EQ(5u, effect_tree.size());
 
   const cc::EffectNode& converted_root_effect = *effect_tree.Node(1);
   EXPECT_EQ(-1, converted_root_effect.parent_id);
-  EXPECT_EQ(CompositorElementIdFromUniqueObjectId(1).GetInternalValue(),
-            converted_root_effect.stable_id);
+  EXPECT_EQ(root_stable_id, converted_root_effect.stable_id);
 
   const cc::EffectNode& converted_effect1 = *effect_tree.Node(2);
   EXPECT_EQ(converted_root_effect.id, converted_effect1.parent_id);
   EXPECT_FLOAT_EQ(0.5, converted_effect1.opacity);
-  EXPECT_EQ(2u, converted_effect1.stable_id);
+  EXPECT_EQ(real_effect1->GetCompositorElementId().GetInternalValue(),
+            converted_effect1.stable_id);
 
   const cc::EffectNode& converted_effect2 = *effect_tree.Node(3);
   EXPECT_EQ(converted_effect1.id, converted_effect2.parent_id);
@@ -2348,8 +2347,8 @@
 }
 
 TEST_P(PaintArtifactCompositorTest, UpdatePopulatesCompositedElementIds) {
-  auto transform = CreateSampleTransformNodeWithElementId();
-  auto effect = CreateSampleEffectNodeWithElementId();
+  auto transform = CreateAnimatingTransform(t0());
+  auto effect = CreateAnimatingOpacityEffect(e0());
   TestPaintArtifact artifact;
   artifact.Chunk(*transform, c0(), e0())
       .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack)
@@ -2369,35 +2368,26 @@
 // included in the composited element id set returned from
 // |PaintArtifactCompositor::Update(...)|.
 TEST_P(PaintArtifactCompositorTest, UniqueAnimationCompositedElementIds) {
-  TransformPaintPropertyNode::State transform_state;
-  transform_state.direct_compositing_reasons =
-      CompositingReason::kActiveTransformAnimation;
-  transform_state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
-      31, CompositorElementIdNamespace::kPrimaryTransform);
-  auto transform =
-      TransformPaintPropertyNode::Create(t0(), std::move(transform_state));
-
-  EffectPaintPropertyNode::State effect_state;
-  effect_state.local_transform_space = transform;
-  effect_state.output_clip = &c0();
-  effect_state.opacity = 2.0 / 255.0;
-  effect_state.direct_compositing_reasons =
-      CompositingReason::kActiveOpacityAnimation;
-  effect_state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
-      41, CompositorElementIdNamespace::kPrimaryEffect);
-  auto effect = EffectPaintPropertyNode::Create(e0(), std::move(effect_state));
-
-  TestPaintArtifact artifact;
-  artifact.Chunk(*transform, c0(), *effect)
-      .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+  auto animating_transform = CreateAnimatingTransform(t0());
+  auto non_animating_transform = CreateTransform(
+      *animating_transform, TransformationMatrix().Translate(10, 20));
+  auto animating_effect = CreateAnimatingOpacityEffect(e0());
+  auto non_animating_effect = CreateOpacityEffect(*animating_effect, 0.5f);
 
   CompositorElementIdSet composited_element_ids;
-  Update(artifact.Build(), composited_element_ids);
+  Update(TestPaintArtifact()
+             .Chunk(*animating_transform, c0(), *animating_effect)
+             .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack)
+             .Chunk(*non_animating_transform, c0(), *non_animating_effect)
+             .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack)
+             .Build(),
+         composited_element_ids);
 
   EXPECT_EQ(2u, composited_element_ids.size());
-  EXPECT_TRUE(
-      composited_element_ids.count(transform->GetCompositorElementId()));
-  EXPECT_TRUE(composited_element_ids.count(effect->GetCompositorElementId()));
+  EXPECT_EQ(1u, composited_element_ids.count(
+                    animating_transform->GetCompositorElementId()));
+  EXPECT_EQ(1u, composited_element_ids.count(
+                    animating_effect->GetCompositorElementId()));
 }
 
 TEST_P(PaintArtifactCompositorTest, SkipChunkWithOpacityZero) {
@@ -2551,7 +2541,7 @@
 }
 
 TEST_P(PaintArtifactCompositorTest, UpdateManagesLayerElementIds) {
-  auto transform = CreateSampleTransformNodeWithElementId();
+  auto transform = CreateAnimatingTransform(t0());
   CompositorElementId element_id = transform->GetCompositorElementId();
 
   {
@@ -3442,13 +3432,13 @@
   //    aa ab L2 L3   ca          (L = layer)
   //    |   |          |
   //   L0  L1         L5
-  auto e = CreateCompositedAnimatingOpacityEffect(e0(), 1.f);
-  auto a = CreateCompositedAnimatingOpacityEffect(*e, 1.f);
-  auto b = CreateCompositedAnimatingOpacityEffect(*e, 1.f);
-  auto c = CreateCompositedAnimatingOpacityEffect(*e, 1.f);
-  auto aa = CreateCompositedAnimatingOpacityEffect(*a, 1.f);
-  auto ab = CreateCompositedAnimatingOpacityEffect(*a, 1.f);
-  auto ca = CreateCompositedAnimatingOpacityEffect(*c, 1.f);
+  auto e = CreateAnimatingOpacityEffect(e0());
+  auto a = CreateAnimatingOpacityEffect(*e);
+  auto b = CreateAnimatingOpacityEffect(*e);
+  auto c = CreateAnimatingOpacityEffect(*e);
+  auto aa = CreateAnimatingOpacityEffect(*a);
+  auto ab = CreateAnimatingOpacityEffect(*a);
+  auto ca = CreateAnimatingOpacityEffect(*c);
   auto t = CreateTransform(t0(), TransformationMatrix().Rotate(90),
                            FloatPoint3D(), CompositingReason::k3DTransform);
 
@@ -3569,11 +3559,10 @@
 
 TEST_P(PaintArtifactCompositorTest,
        OpacityIndirectlyAffectingTwoLayersWithOpacityAnimations) {
-  auto opacity = CreateCompositedAnimatingOpacityEffect(e0(), 1.f);
-  auto child_composited_effect =
-      CreateCompositedAnimatingOpacityEffect(*opacity, 1.f);
+  auto opacity = CreateAnimatingOpacityEffect(e0());
+  auto child_composited_effect = CreateAnimatingOpacityEffect(*opacity);
   auto grandchild_composited_effect =
-      CreateCompositedAnimatingOpacityEffect(*child_composited_effect, 1.f);
+      CreateAnimatingOpacityEffect(*child_composited_effect);
 
   TestPaintArtifact artifact;
   artifact.Chunk(t0(), c0(), *child_composited_effect)
@@ -3612,13 +3601,8 @@
                  kHasRenderSurface);
 }
 
-TEST_P(PaintArtifactCompositorTest,
-       FilterCompositedAnimationCreatesRenderSurface) {
-  EffectPaintPropertyNode::State state;
-  state.local_transform_space = &t0();
-  state.direct_compositing_reasons = CompositingReason::kActiveFilterAnimation;
-  state.is_running_filter_animation_on_compositor = true;
-  auto e1 = EffectPaintPropertyNode::Create(e0(), std::move(state));
+TEST_P(PaintArtifactCompositorTest, FilterAnimationCreatesRenderSurface) {
+  auto e1 = CreateAnimatingFilterEffect(e0());
   Update(TestPaintArtifact()
              .Chunk(t0(), c0(), *e1)
              .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite)
@@ -3628,20 +3612,6 @@
                  kHasRenderSurface);
 }
 
-TEST_P(PaintArtifactCompositorTest,
-       FilterNonCompositedAnimationDoesNotCreateRenderSurface) {
-  EffectPaintPropertyNode::State state;
-  state.local_transform_space = &t0();
-  state.direct_compositing_reasons = CompositingReason::kActiveFilterAnimation;
-  auto e1 = EffectPaintPropertyNode::Create(e0(), std::move(state));
-  Update(TestPaintArtifact()
-             .Chunk(t0(), c0(), *e1)
-             .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite)
-             .Build());
-  ASSERT_EQ(1u, ContentLayerCount());
-  EXPECT_OPACITY(ContentLayerAt(0)->effect_tree_index(), 1.f, kNoRenderSurface);
-}
-
 TEST_P(PaintArtifactCompositorTest, BackdropFilterCreatesRenderSurface) {
   CompositorFilterOperations filter;
   filter.AppendBlurFilter(5);
@@ -3658,13 +3628,8 @@
 }
 
 TEST_P(PaintArtifactCompositorTest,
-       BackdropFilterCompositedAnimationCreatesRenderSurface) {
-  EffectPaintPropertyNode::State state;
-  state.local_transform_space = &t0();
-  state.direct_compositing_reasons =
-      CompositingReason::kActiveBackdropFilterAnimation;
-  state.is_running_backdrop_filter_animation_on_compositor = true;
-  auto e1 = EffectPaintPropertyNode::Create(e0(), std::move(state));
+       BackdropFilterAnimationCreatesRenderSurface) {
+  auto e1 = CreateAnimatingBackdropFilterEffect(e0());
   Update(TestPaintArtifact()
              .Chunk(t0(), c0(), *e1)
              .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite)
@@ -3674,21 +3639,6 @@
                  kHasRenderSurface);
 }
 
-TEST_P(PaintArtifactCompositorTest,
-       BackdropFilterNonCompositedAnimationCreatesRenderSurface) {
-  EffectPaintPropertyNode::State state;
-  state.local_transform_space = &t0();
-  state.direct_compositing_reasons =
-      CompositingReason::kActiveBackdropFilterAnimation;
-  auto e1 = EffectPaintPropertyNode::Create(e0(), std::move(state));
-  Update(TestPaintArtifact()
-             .Chunk(t0(), c0(), *e1)
-             .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite)
-             .Build());
-  ASSERT_EQ(1u, ContentLayerCount());
-  EXPECT_OPACITY(ContentLayerAt(0)->effect_tree_index(), 1.f, kNoRenderSurface);
-}
-
 TEST_P(PaintArtifactCompositorTest, Non2dAxisAlignedClip) {
   auto rotate = CreateTransform(t0(), TransformationMatrix().Rotate(45));
   auto clip = CreateClip(c0(), *rotate, FloatRoundedRect(50, 50, 50, 50));
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index b4978436..d503210 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -729,9 +729,9 @@
   // is under the blend mode. This value is adjusted in PaintArtifactCompositor
   // ::UpdateRenderSurfaceForEffects() to account for more than one layer.
   if (!next_effect.Filter().IsEmpty() ||
-      next_effect.IsRunningFilterAnimationOnCompositor() ||
+      next_effect.HasActiveFilterAnimation() ||
       !next_effect.BackdropFilter().IsEmpty() ||
-      next_effect.IsRunningBackdropFilterAnimationOnCompositor() ||
+      next_effect.HasActiveBackdropFilterAnimation() ||
       (used_blend_mode != SkBlendMode::kSrcOver &&
        used_blend_mode != SkBlendMode::kDstIn))
     effect_node.has_render_surface = true;
diff --git a/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc b/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc
index fd85381..48aa0cf2 100644
--- a/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc
@@ -86,7 +86,7 @@
     ImageDecodingStore::Instance().SetCacheLimitInBytes(1024 * 1024);
     data_ = SharedBuffer::Create(kWhitePNG, sizeof(kWhitePNG));
     frame_count_ = 1;
-    std::unique_ptr<MockImageDecoder> decoder = MockImageDecoder::Create(this);
+    auto decoder = std::make_unique<MockImageDecoder>(this);
     actual_decoder_ = decoder.get();
     actual_decoder_->SetSize(1, 1);
     lazy_decoder_ = DeferredImageDecoder::CreateForTesting(std::move(decoder));
diff --git a/third_party/blink/renderer/platform/graphics/generated_image.cc b/third_party/blink/renderer/platform/graphics/generated_image.cc
index a0b90a2..b46ffab8 100644
--- a/third_party/blink/renderer/platform/graphics/generated_image.cc
+++ b/third_party/blink/renderer/platform/graphics/generated_image.cc
@@ -48,7 +48,7 @@
   FloatRect tile_rect = src_rect;
   tile_rect.Expand(repeat_spacing);
 
-  std::unique_ptr<PaintController> paint_controller = PaintController::Create();
+  auto paint_controller = std::make_unique<PaintController>();
   GraphicsContext context(*paint_controller);
   context.BeginRecording(tile_rect);
   DrawTile(context, src_rect);
diff --git a/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.cc b/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.cc
index e155e172..595db750 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.cc
@@ -15,11 +15,6 @@
 
 namespace blink {
 
-std::unique_ptr<SharedContextRateLimiter> SharedContextRateLimiter::Create(
-    unsigned max_pending_ticks) {
-  return base::WrapUnique(new SharedContextRateLimiter(max_pending_ticks));
-}
-
 SharedContextRateLimiter::SharedContextRateLimiter(unsigned max_pending_ticks)
     : max_pending_ticks_(max_pending_ticks), can_use_sync_queries_(false) {
   context_provider_ =
diff --git a/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h b/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h
index 0a13f77..006ef78 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h
@@ -40,14 +40,12 @@
   USING_FAST_MALLOC(SharedContextRateLimiter);
 
  public:
-  static std::unique_ptr<SharedContextRateLimiter> Create(
-      unsigned max_pending_ticks);
+  explicit SharedContextRateLimiter(unsigned max_pending_ticks);
+
   void Tick();
   void Reset();
 
  private:
-  SharedContextRateLimiter(unsigned max_pending_ticks);
-
   std::unique_ptr<WebGraphicsContext3DProvider> context_provider_;
   Deque<GLuint> queries_;
   unsigned max_pending_ticks_;
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index 5cc3ac01..386e77a 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -105,7 +105,7 @@
       in_drawing_recorder_(false) {
   // FIXME: Do some tests to determine how many states are typically used, and
   // allocate several here.
-  paint_state_stack_.push_back(GraphicsContextState::Create());
+  paint_state_stack_.push_back(std::make_unique<GraphicsContextState>());
   paint_state_ = paint_state_stack_.back().get();
 
   if (ContextDisabled()) {
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context_state.h b/third_party/blink/renderer/platform/graphics/graphics_context_state.h
index abeb92c7..6e19b824 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context_state.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_context_state.h
@@ -48,15 +48,13 @@
   USING_FAST_MALLOC(GraphicsContextState);
 
  public:
-  static std::unique_ptr<GraphicsContextState> Create() {
-    return base::WrapUnique(new GraphicsContextState());
-  }
-
   static std::unique_ptr<GraphicsContextState> CreateAndCopy(
       const GraphicsContextState& other) {
     return base::WrapUnique(new GraphicsContextState(other));
   }
 
+  GraphicsContextState();
+
   void Copy(const GraphicsContextState&);
 
   // PaintFlags objects that reflect the current state. If the length of the
@@ -114,7 +112,6 @@
   void SetShouldAntialias(bool);
 
  private:
-  GraphicsContextState();
   explicit GraphicsContextState(const GraphicsContextState&);
   GraphicsContextState& operator=(const GraphicsContextState&) = delete;
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context_test.cc b/third_party/blink/renderer/platform/graphics/graphics_context_test.cc
index d43fd91..b0ec9893 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context_test.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context_test.cc
@@ -73,7 +73,7 @@
   bitmap.eraseColor(0);
   SkiaPaintCanvas canvas(bitmap);
 
-  std::unique_ptr<PaintController> paint_controller = PaintController::Create();
+  auto paint_controller = std::make_unique<PaintController>();
   GraphicsContext context(*paint_controller);
 
   Color opaque(1.0f, 0.0f, 0.0f, 1.0f);
@@ -104,7 +104,7 @@
   Color alpha(0.0f, 0.0f, 0.0f, 0.0f);
   FloatRect bounds(0, 0, 100, 100);
 
-  std::unique_ptr<PaintController> paint_controller = PaintController::Create();
+  auto paint_controller = std::make_unique<PaintController>();
   GraphicsContext context(*paint_controller);
   context.BeginRecording(bounds);
 
@@ -149,7 +149,7 @@
     bitmap_.allocN32Pixels(4, 1);
     bitmap_.eraseColor(0);
     canvas_ = std::make_unique<SkiaPaintCanvas>(bitmap_);
-    paint_controller_ = PaintController::Create();
+    paint_controller_ = std::make_unique<PaintController>();
     context_ = std::make_unique<GraphicsContext>(*paint_controller_);
     context_->BeginRecording(FloatRect(0, 0, 4, 1));
   }
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
index c915f3456..02832cc9 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -69,11 +69,6 @@
 
 namespace blink {
 
-std::unique_ptr<GraphicsLayer> GraphicsLayer::Create(
-    GraphicsLayerClient& client) {
-  return base::WrapUnique(new GraphicsLayer(client));
-}
-
 GraphicsLayer::GraphicsLayer(GraphicsLayerClient& client)
     : client_(client),
       prevent_contents_opaque_changes_(false),
@@ -1014,7 +1009,7 @@
 PaintController& GraphicsLayer::GetPaintController() const {
   CHECK(PaintsContentOrHitTest());
   if (!paint_controller_)
-    paint_controller_ = PaintController::Create();
+    paint_controller_ = std::make_unique<PaintController>();
   return *paint_controller_;
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.h b/third_party/blink/renderer/platform/graphics/graphics_layer.h
index 86eaa150..a4b051e3 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -84,8 +84,7 @@
   USING_FAST_MALLOC(GraphicsLayer);
 
  public:
-  static std::unique_ptr<GraphicsLayer> Create(GraphicsLayerClient&);
-
+  explicit GraphicsLayer(GraphicsLayerClient&);
   ~GraphicsLayer() override;
 
   GraphicsLayerClient& Client() const { return client_; }
@@ -324,8 +323,6 @@
  protected:
   String DebugName(cc::Layer*) const;
 
-  explicit GraphicsLayer(GraphicsLayerClient&);
-
  private:
   friend class CompositedLayerMappingTest;
   friend class GraphicsLayerTest;
diff --git a/third_party/blink/renderer/platform/graphics/image_decoding_store.cc b/third_party/blink/renderer/platform/graphics/image_decoding_store.cc
index d8a8b910..30ec2ba 100644
--- a/third_party/blink/renderer/platform/graphics/image_decoding_store.cc
+++ b/third_party/blink/renderer/platform/graphics/image_decoding_store.cc
@@ -108,8 +108,8 @@
   // Prune old cache entries to give space for the new one.
   Prune();
 
-  std::unique_ptr<DecoderCacheEntry> new_cache_entry =
-      DecoderCacheEntry::Create(generator, std::move(decoder), client_id);
+  auto new_cache_entry = std::make_unique<DecoderCacheEntry>(
+      generator, 0, std::move(decoder), client_id);
 
   MutexLocker lock(mutex_);
   DCHECK(!decoder_cache_map_.Contains(new_cache_entry->CacheKey()));
diff --git a/third_party/blink/renderer/platform/graphics/image_decoding_store.h b/third_party/blink/renderer/platform/graphics/image_decoding_store.h
index a97e28f..7f239dd 100644
--- a/third_party/blink/renderer/platform/graphics/image_decoding_store.h
+++ b/third_party/blink/renderer/platform/graphics/image_decoding_store.h
@@ -118,13 +118,16 @@
 
 class DecoderCacheEntry final : public CacheEntry {
  public:
-  static std::unique_ptr<DecoderCacheEntry> Create(
-      const ImageFrameGenerator* generator,
-      std::unique_ptr<ImageDecoder> decoder,
-      cc::PaintImage::GeneratorClientId client_id) {
-    return base::WrapUnique(
-        new DecoderCacheEntry(generator, 0, std::move(decoder), client_id));
-  }
+  DecoderCacheEntry(const ImageFrameGenerator* generator,
+                    int count,
+                    std::unique_ptr<ImageDecoder> decoder,
+                    cc::PaintImage::GeneratorClientId client_id)
+      : CacheEntry(generator, count),
+        cached_decoder_(std::move(decoder)),
+        size_(SkISize::Make(cached_decoder_->DecodedSize().Width(),
+                            cached_decoder_->DecodedSize().Height())),
+        alpha_option_(cached_decoder_->GetAlphaOption()),
+        client_id_(client_id) {}
 
   size_t MemoryUsageInBytes() const override {
     return size_.width() * size_.height() * 4;
@@ -158,17 +161,6 @@
   ImageDecoder* CachedDecoder() const { return cached_decoder_.get(); }
 
  private:
-  DecoderCacheEntry(const ImageFrameGenerator* generator,
-                    int count,
-                    std::unique_ptr<ImageDecoder> decoder,
-                    cc::PaintImage::GeneratorClientId client_id)
-      : CacheEntry(generator, count),
-        cached_decoder_(std::move(decoder)),
-        size_(SkISize::Make(cached_decoder_->DecodedSize().Width(),
-                            cached_decoder_->DecodedSize().Height())),
-        alpha_option_(cached_decoder_->GetAlphaOption()),
-        client_id_(client_id) {}
-
   std::unique_ptr<ImageDecoder> cached_decoder_;
   SkISize size_;
   ImageDecoder::AlphaOption alpha_option_;
@@ -252,9 +244,7 @@
   USING_FAST_MALLOC(ImageDecodingStore);
 
  public:
-  static std::unique_ptr<ImageDecodingStore> Create() {
-    return base::WrapUnique(new ImageDecodingStore);
-  }
+  ImageDecodingStore();
   ~ImageDecodingStore();
 
   static ImageDecodingStore& Instance();
@@ -287,8 +277,6 @@
   int DecoderCacheEntries();
 
  private:
-  ImageDecodingStore();
-
   void Prune();
 
   // Called by the memory pressure listener when the memory pressure rises.
diff --git a/third_party/blink/renderer/platform/graphics/image_decoding_store_test.cc b/third_party/blink/renderer/platform/graphics/image_decoding_store_test.cc
index ad632ecc..37aba75e 100644
--- a/third_party/blink/renderer/platform/graphics/image_decoding_store_test.cc
+++ b/third_party/blink/renderer/platform/graphics/image_decoding_store_test.cc
@@ -77,7 +77,7 @@
 
 TEST_F(ImageDecodingStoreTest, insertDecoder) {
   const SkISize size = SkISize::Make(1, 1);
-  std::unique_ptr<ImageDecoder> decoder = MockImageDecoder::Create(this);
+  auto decoder = std::make_unique<MockImageDecoder>(this);
   decoder->SetSize(1, 1);
   const ImageDecoder* ref_decoder = decoder.get();
   ImageDecodingStore::Instance().InsertDecoder(
@@ -99,9 +99,9 @@
 }
 
 TEST_F(ImageDecodingStoreTest, evictDecoder) {
-  std::unique_ptr<ImageDecoder> decoder1 = MockImageDecoder::Create(this);
-  std::unique_ptr<ImageDecoder> decoder2 = MockImageDecoder::Create(this);
-  std::unique_ptr<ImageDecoder> decoder3 = MockImageDecoder::Create(this);
+  auto decoder1 = std::make_unique<MockImageDecoder>(this);
+  auto decoder2 = std::make_unique<MockImageDecoder>(this);
+  auto decoder3 = std::make_unique<MockImageDecoder>(this);
   decoder1->SetSize(1, 1);
   decoder2->SetSize(2, 2);
   decoder3->SetSize(3, 3);
@@ -131,9 +131,9 @@
 }
 
 TEST_F(ImageDecodingStoreTest, decoderInUseNotEvicted) {
-  std::unique_ptr<ImageDecoder> decoder1 = MockImageDecoder::Create(this);
-  std::unique_ptr<ImageDecoder> decoder2 = MockImageDecoder::Create(this);
-  std::unique_ptr<ImageDecoder> decoder3 = MockImageDecoder::Create(this);
+  auto decoder1 = std::make_unique<MockImageDecoder>(this);
+  auto decoder2 = std::make_unique<MockImageDecoder>(this);
+  auto decoder3 = std::make_unique<MockImageDecoder>(this);
   decoder1->SetSize(1, 1);
   decoder2->SetSize(2, 2);
   decoder3->SetSize(3, 3);
@@ -169,7 +169,7 @@
 
 TEST_F(ImageDecodingStoreTest, removeDecoder) {
   const SkISize size = SkISize::Make(1, 1);
-  std::unique_ptr<ImageDecoder> decoder = MockImageDecoder::Create(this);
+  auto decoder = std::make_unique<MockImageDecoder>(this);
   decoder->SetSize(1, 1);
   const ImageDecoder* ref_decoder = decoder.get();
   ImageDecodingStore::Instance().InsertDecoder(
@@ -200,7 +200,7 @@
 
   const SkISize size = SkISize::Make(1, 1);
 
-  std::unique_ptr<ImageDecoder> decoder = MockImageDecoder::Create(this);
+  auto decoder = std::make_unique<MockImageDecoder>(this);
   ImageDecoder* decoder_1 = decoder.get();
   decoder_1->SetSize(1, 1);
   auto client_id_1 = cc::PaintImage::GetNextGeneratorClientId();
@@ -208,7 +208,7 @@
                                                std::move(decoder));
   EXPECT_EQ(ImageDecodingStore::Instance().CacheEntries(), 1);
 
-  decoder = MockImageDecoder::Create(this);
+  decoder = std::make_unique<MockImageDecoder>(this);
   ImageDecoder* decoder_2 = decoder.get();
   decoder_2->SetSize(1, 1);
   auto client_id_2 = cc::PaintImage::GetNextGeneratorClientId();
@@ -235,7 +235,7 @@
 }
 
 TEST_F(ImageDecodingStoreTest, OnMemoryPressure) {
-  std::unique_ptr<ImageDecoder> decoder = MockImageDecoder::Create(this);
+  auto decoder = std::make_unique<MockImageDecoder>(this);
   decoder->SetSize(1, 1);
   ImageDecodingStore::Instance().InsertDecoder(
       generator_.get(), cc::PaintImage::kDefaultGeneratorClientId,
diff --git a/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h
index b6c65514..36af18b 100644
--- a/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h
@@ -46,7 +46,7 @@
              direct_compositing_reasons == other.direct_compositing_reasons;
     }
 
-    PaintPropertyChangeType CheckChange(const State& other) const {
+    PaintPropertyChangeType ComputeChange(const State& other) const {
       if (!EqualIgnoringHitTestRects(other) ||
           clip_rect_excluding_overlay_scrollbars !=
               other.clip_rect_excluding_overlay_scrollbars) {
@@ -73,10 +73,14 @@
         true /* is_parent_alias */));
   }
 
+  // The empty AnimationState struct is to meet the requirement of
+  // ObjectPaintProperties.
+  struct AnimationState {};
   PaintPropertyChangeType Update(const ClipPaintPropertyNode& parent,
-                                 State&& state) {
+                                 State&& state,
+                                 const AnimationState& = AnimationState()) {
     auto parent_changed = SetParent(&parent);
-    auto state_changed = state_.CheckChange(state);
+    auto state_changed = state_.ComputeChange(state);
     if (state_changed != PaintPropertyChangeType::kUnchanged) {
       DCHECK(!IsParentAlias()) << "Changed the state of an alias node.";
       state_ = std::move(state);
diff --git a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
index 624a72c4..84b9b81 100644
--- a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
@@ -27,6 +27,13 @@
 class PLATFORM_EXPORT EffectPaintPropertyNode
     : public PaintPropertyNode<EffectPaintPropertyNode> {
  public:
+  struct AnimationState {
+    AnimationState() {}
+    bool is_running_opacity_animation_on_compositor = false;
+    bool is_running_filter_animation_on_compositor = false;
+    bool is_running_backdrop_filter_animation_on_compositor = false;
+  };
+
   // To make it less verbose and more readable to construct and update a node,
   // a struct with default values is used to represent the state.
   struct State {
@@ -49,44 +56,37 @@
     gfx::RRectF backdrop_filter_bounds;
     SkBlendMode blend_mode = SkBlendMode::kSrcOver;
     // === End of effects ===
-    // TODO(crbug.com/937929): Put these into CompositingReasons when we can
-    // detect composited animation status changes in LayoutObject::SetStyle().
-    bool is_running_opacity_animation_on_compositor = false;
-    bool is_running_filter_animation_on_compositor = false;
-    bool is_running_backdrop_filter_animation_on_compositor = false;
     CompositingReasons direct_compositing_reasons = CompositingReason::kNone;
     CompositorElementId compositor_element_id;
     // The offset of the origin of filters in local_transform_space.
     FloatPoint filters_origin;
 
-    PaintPropertyChangeType CheckChange(const State& other) {
+    PaintPropertyChangeType ComputeChange(
+        const State& other,
+        const AnimationState& animation_state) {
       if (local_transform_space != other.local_transform_space ||
           output_clip != other.output_clip ||
           color_filter != other.color_filter ||
           backdrop_filter_bounds != other.backdrop_filter_bounds ||
           blend_mode != other.blend_mode ||
-          is_running_opacity_animation_on_compositor !=
-              other.is_running_opacity_animation_on_compositor ||
-          is_running_filter_animation_on_compositor !=
-              other.is_running_filter_animation_on_compositor ||
-          is_running_backdrop_filter_animation_on_compositor !=
-              other.is_running_backdrop_filter_animation_on_compositor ||
           direct_compositing_reasons != other.direct_compositing_reasons ||
           compositor_element_id != other.compositor_element_id ||
           filters_origin != other.filters_origin) {
         return PaintPropertyChangeType::kChangedOnlyValues;
       }
       bool opacity_changed = opacity != other.opacity;
-      if (opacity_changed && !is_running_opacity_animation_on_compositor) {
+      if (opacity_changed &&
+          !animation_state.is_running_opacity_animation_on_compositor) {
         return PaintPropertyChangeType::kChangedOnlyValues;
       }
       bool filter_changed = filter != other.filter;
-      if (filter_changed && !is_running_filter_animation_on_compositor) {
+      if (filter_changed &&
+          !animation_state.is_running_filter_animation_on_compositor) {
         return PaintPropertyChangeType::kChangedOnlyValues;
       }
       bool backdrop_filter_changed = backdrop_filter != other.backdrop_filter;
       if (backdrop_filter_changed &&
-          !is_running_backdrop_filter_animation_on_compositor) {
+          !animation_state.is_running_backdrop_filter_animation_on_compositor) {
         return PaintPropertyChangeType::kChangedOnlyValues;
       }
       if (opacity_changed || filter_changed || backdrop_filter_changed) {
@@ -111,15 +111,16 @@
         &parent, State{}, true /* is_parent_alias */));
   }
 
-  PaintPropertyChangeType Update(const EffectPaintPropertyNode& parent,
-                                 State&& state) {
+  PaintPropertyChangeType Update(
+      const EffectPaintPropertyNode& parent,
+      State&& state,
+      const AnimationState& animation_state = AnimationState()) {
     auto parent_changed = SetParent(&parent);
-    auto state_changed = state_.CheckChange(state);
+    auto state_changed = state_.ComputeChange(state, animation_state);
     if (state_changed != PaintPropertyChangeType::kUnchanged) {
       DCHECK(!IsParentAlias()) << "Changed the state of an alias node.";
       state_ = std::move(state);
       SetChanged();
-      Validate();
     }
     return std::max(parent_changed, state_changed);
   }
@@ -187,31 +188,24 @@
     return DirectCompositingReasons() != CompositingReason::kNone;
   }
 
-  // The difference between the following two functions is that the former
-  // is also true for animations that the compositor are not aware of (e.g.
-  // paused animations and worklet animations), while the latter is true only if
-  // the compositor is handling the animation.
+  // TODO(crbug.com/900241): Use HaveActiveXXXAnimation() instead of this
+  // function when we can track animations for each property type.
+  bool RequiresCompositingForAnimation() const {
+    return DirectCompositingReasons() &
+           CompositingReason::kComboActiveAnimation;
+  }
   bool HasActiveOpacityAnimation() const {
     return DirectCompositingReasons() &
            CompositingReason::kActiveOpacityAnimation;
   }
-  bool IsRunningOpacityAnimationOnCompositor() const {
-    return state_.is_running_opacity_animation_on_compositor;
-  }
   bool HasActiveFilterAnimation() const {
     return DirectCompositingReasons() &
            CompositingReason::kActiveFilterAnimation;
   }
-  bool IsRunningFilterAnimationOnCompositor() const {
-    return state_.is_running_filter_animation_on_compositor;
-  }
   bool HasActiveBackdropFilterAnimation() const {
     return DirectCompositingReasons() &
            CompositingReason::kActiveBackdropFilterAnimation;
   }
-  bool IsRunningBackdropFilterAnimationOnCompositor() const {
-    return state_.is_running_backdrop_filter_animation_on_compositor;
-  }
 
   const CompositorElementId& GetCompositorElementId() const {
     DCHECK(!Parent() || !IsParentAlias());
@@ -234,15 +228,6 @@
     return state_.direct_compositing_reasons;
   }
 
-  void Validate() const {
-    DCHECK(!IsRunningOpacityAnimationOnCompositor() ||
-           HasActiveOpacityAnimation());
-    DCHECK(!IsRunningFilterAnimationOnCompositor() ||
-           HasActiveFilterAnimation());
-    DCHECK(!IsRunningBackdropFilterAnimationOnCompositor() ||
-           HasActiveBackdropFilterAnimation());
-  }
-
   State state_;
 };
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
index fcb29cbb..f0d099de 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
@@ -63,11 +63,7 @@
     kTransient,
   };
 
-  static std::unique_ptr<PaintController> Create(
-      Usage usage = kMultiplePaints) {
-    return base::WrapUnique(new PaintController(usage));
-  }
-
+  explicit PaintController(Usage = kMultiplePaints);
   ~PaintController();
 
   // For pre-PaintAfterPaint only.
@@ -255,8 +251,6 @@
   friend class PaintControllerTestBase;
   friend class PaintControllerPaintTestBase;
 
-  PaintController(Usage);
-
   // True if all display items associated with the client are validly cached.
   // However, the current algorithm allows the following situations even if
   // ClientCacheIsValid() is true for a client during painting:
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
index 861ec09..50aee41 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
@@ -1577,7 +1577,8 @@
 }
 
 TEST_P(PaintControllerTest, TransientPaintControllerIncompleteCycle) {
-  auto paint_controller = PaintController::Create(PaintController::kTransient);
+  auto paint_controller =
+      std::make_unique<PaintController>(PaintController::kTransient);
   GraphicsContext context(*paint_controller);
   FakeDisplayItemClient client("client", LayoutRect(100, 100, 50, 50));
   InitRootChunk(*paint_controller);
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h
index e4def4a..c51dc84 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h
@@ -42,7 +42,7 @@
       : root_paint_property_client_("root"),
         root_paint_chunk_id_(root_paint_property_client_,
                              DisplayItem::kUninitializedType),
-        paint_controller_(PaintController::Create()) {}
+        paint_controller_(std::make_unique<PaintController>()) {}
 
   void InitRootChunk() { InitRootChunk(GetPaintController()); }
   void InitRootChunk(PaintController& paint_controller) {
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc
index 1e22754..7f25b82 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc
@@ -23,7 +23,7 @@
     paint_controller_ = paint_controller;
   } else {
     own_paint_controller_ =
-        PaintController::Create(PaintController::kTransient);
+        std::make_unique<PaintController>(PaintController::kTransient);
     paint_controller_ = own_paint_controller_.get();
   }
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
index c75867ba..39c9aa4 100644
--- a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
@@ -55,7 +55,7 @@
         cc::OverscrollBehavior::kOverscrollBehaviorTypeAuto);
     base::Optional<cc::SnapContainerData> snap_container_data;
 
-    PaintPropertyChangeType CheckChange(const State& other) const {
+    PaintPropertyChangeType ComputeChange(const State& other) const {
       if (container_rect != other.container_rect ||
           contents_size != other.contents_size ||
           user_scrollable_horizontal != other.user_scrollable_horizontal ||
@@ -91,10 +91,14 @@
     return nullptr;
   }
 
+  // The empty AnimationState struct is to meet the requirement of
+  // ObjectPaintProperties.
+  struct AnimationState {};
   PaintPropertyChangeType Update(const ScrollPaintPropertyNode& parent,
-                                 State&& state) {
+                                 State&& state,
+                                 const AnimationState& = AnimationState()) {
     auto parent_changed = SetParent(&parent);
-    auto state_changed = state_.CheckChange(state);
+    auto state_changed = state_.ComputeChange(state);
     if (state_changed != PaintPropertyChangeType::kUnchanged) {
       state_ = std::move(state);
       Validate();
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
index e5a651b4..1510db5b 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
@@ -101,6 +101,11 @@
     std::unique_ptr<MatrixAndOrigin> matrix_and_origin_;
   };
 
+  struct AnimationState {
+    AnimationState() {}
+    bool is_running_animation_on_compositor = false;
+  };
+
   // To make it less verbose and more readable to construct and update a node,
   // a struct with default values is used to represent the state.
   struct State {
@@ -108,23 +113,20 @@
     scoped_refptr<const ScrollPaintPropertyNode> scroll;
     bool flattens_inherited_transform = false;
     bool affected_by_outer_viewport_bounds_delta = false;
-    // TODO(crbug.com/937929): Put this into CompositingReasons when we can
-    // detect composited animation status changes in LayoutObject::SetStyle().
-    bool is_running_animation_on_compositor = false;
     BackfaceVisibility backface_visibility = BackfaceVisibility::kInherited;
     unsigned rendering_context_id = 0;
     CompositingReasons direct_compositing_reasons = CompositingReason::kNone;
     CompositorElementId compositor_element_id;
     std::unique_ptr<CompositorStickyConstraint> sticky_constraint;
 
-    PaintPropertyChangeType CheckChange(const State& other) const {
+    PaintPropertyChangeType ComputeChange(
+        const State& other,
+        const AnimationState& animation_state) const {
       if (transform_and_origin.Origin() !=
               other.transform_and_origin.Origin() ||
           flattens_inherited_transform != other.flattens_inherited_transform ||
           affected_by_outer_viewport_bounds_delta !=
               other.affected_by_outer_viewport_bounds_delta ||
-          is_running_animation_on_compositor !=
-              other.is_running_animation_on_compositor ||
           backface_visibility != other.backface_visibility ||
           rendering_context_id != other.rendering_context_id ||
           direct_compositing_reasons != other.direct_compositing_reasons ||
@@ -136,7 +138,7 @@
         return PaintPropertyChangeType::kChangedOnlyValues;
       }
       if (!transform_and_origin.TransformEquals(other.transform_and_origin)) {
-        return is_running_animation_on_compositor
+        return animation_state.is_running_animation_on_compositor
                    ? PaintPropertyChangeType::
                          kChangedOnlyCompositedAnimationValues
                    : PaintPropertyChangeType::kChangedOnlyValues;
@@ -168,10 +170,12 @@
         &parent, State{}, true /* is_parent_alias */));
   }
 
-  PaintPropertyChangeType Update(const TransformPaintPropertyNode& parent,
-                                 State&& state) {
+  PaintPropertyChangeType Update(
+      const TransformPaintPropertyNode& parent,
+      State&& state,
+      const AnimationState& animation_state = AnimationState()) {
     auto parent_changed = SetParent(&parent);
-    auto state_changed = state_.CheckChange(state);
+    auto state_changed = state_.ComputeChange(state, animation_state);
     if (state_changed != PaintPropertyChangeType::kUnchanged) {
       DCHECK(!IsParentAlias()) << "Changed the state of an alias node.";
       state_ = std::move(state);
@@ -258,17 +262,16 @@
     return DirectCompositingReasons() != CompositingReason::kNone;
   }
 
-  // The difference between the following two functions is that the former
-  // is also true for animations that the compositor are not aware of (e.g.
-  // paused animations and worklet animations), while the latter is true only if
-  // the compositor is handling the animation.
+  // TODO(crbug.com/900241): Use HaveActiveTransformAnimation() instead of this
+  // function when we can track animations for each property type.
+  bool RequiresCompositingForAnimation() const {
+    return DirectCompositingReasons() &
+           CompositingReason::kComboActiveAnimation;
+  }
   bool HasActiveTransformAnimation() const {
     return DirectCompositingReasons() &
            CompositingReason::kActiveTransformAnimation;
   }
-  bool IsRunningAnimationOnCompositor() const {
-    return state_.is_running_animation_on_compositor;
-  }
 
   bool RequiresCompositingForRootScroller() const {
     return state_.direct_compositing_reasons & CompositingReason::kRootScroller;
@@ -307,7 +310,6 @@
 #if DCHECK_IS_ON()
     if (IsParentAlias())
       DCHECK(IsIdentity());
-    DCHECK(!IsRunningAnimationOnCompositor() || HasActiveTransformAnimation());
     if (state_.scroll) {
       // If there is an associated scroll node, this can only be a 2d
       // translation for scroll offset.
diff --git a/third_party/blink/renderer/platform/graphics/test/mock_image_decoder.h b/third_party/blink/renderer/platform/graphics/test/mock_image_decoder.h
index 1b29a020..3ee7693 100644
--- a/third_party/blink/renderer/platform/graphics/test/mock_image_decoder.h
+++ b/third_party/blink/renderer/platform/graphics/test/mock_image_decoder.h
@@ -66,10 +66,6 @@
 
 class MockImageDecoder : public ImageDecoder {
  public:
-  static std::unique_ptr<MockImageDecoder> Create(
-      MockImageDecoderClient* client) {
-    return std::make_unique<MockImageDecoder>(client);
-  }
 
   MockImageDecoder(MockImageDecoderClient* client)
       : ImageDecoder(kAlphaPremultiplied,
@@ -157,8 +153,7 @@
   }
 
   std::unique_ptr<ImageDecoder> Create() override {
-    std::unique_ptr<MockImageDecoder> decoder =
-        MockImageDecoder::Create(client_);
+    auto decoder = std::make_unique<MockImageDecoder>(client_);
     decoder->SetSize(decoded_size_.Width(), decoded_size_.Height());
     return std::move(decoder);
   }
diff --git a/third_party/blink/renderer/platform/heap/heap.cc b/third_party/blink/renderer/platform/heap/heap.cc
index 3b62f5c1..666b1200 100644
--- a/third_party/blink/renderer/platform/heap/heap.cc
+++ b/third_party/blink/renderer/platform/heap/heap.cc
@@ -194,7 +194,7 @@
 
 HeapCompact* ThreadHeap::Compaction() {
   if (!compaction_)
-    compaction_ = HeapCompact::Create(this);
+    compaction_ = std::make_unique<HeapCompact>(this);
   return compaction_.get();
 }
 
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.cc b/third_party/blink/renderer/platform/heap/heap_compact.cc
index 5ff6528..cd16fd7 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -32,10 +32,7 @@
   USING_FAST_MALLOC(HeapCompact::MovableObjectFixups);
 
  public:
-  static std::unique_ptr<MovableObjectFixups> Create(ThreadHeap* heap) {
-    return base::WrapUnique(new MovableObjectFixups(heap));
-  }
-
+  explicit MovableObjectFixups(ThreadHeap* heap) : heap_(heap) {}
   ~MovableObjectFixups() = default;
 
   // For the arenas being compacted, record all pages belonging to them.
@@ -57,7 +54,7 @@
     LOG_HEAP_COMPACTION() << "Interior slot: " << slot;
     Address slot_address = reinterpret_cast<Address>(slot);
     if (!interiors_) {
-      interiors_ = SparseHeapBitmap::Create(slot_address);
+      interiors_ = std::make_unique<SparseHeapBitmap>(slot_address);
       return;
     }
     interiors_->Add(slot_address);
@@ -272,8 +269,6 @@
 #endif
 
  private:
-  MovableObjectFixups(ThreadHeap* heap) : heap_(heap) {}
-
   void VerifyUpdatedSlot(MovableReference* slot);
 
   ThreadHeap* heap_;
@@ -353,7 +348,7 @@
 
 HeapCompact::MovableObjectFixups& HeapCompact::Fixups() {
   if (!fixups_)
-    fixups_ = MovableObjectFixups::Create(heap_);
+    fixups_ = std::make_unique<MovableObjectFixups>(heap_);
   return *fixups_;
 }
 
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.h b/third_party/blink/renderer/platform/heap/heap_compact.h
index ec0ef44..c8c31992 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.h
+++ b/third_party/blink/renderer/platform/heap/heap_compact.h
@@ -49,10 +49,7 @@
   friend class incremental_marking_test::IncrementalMarkingTestDriver;
 
  public:
-  static std::unique_ptr<HeapCompact> Create(ThreadHeap* heap) {
-    return base::WrapUnique(new HeapCompact(heap));
-  }
-
+  explicit HeapCompact(ThreadHeap*);
   ~HeapCompact();
 
   // Remove slot from traced_slots_ when a registered slot is destructed by
@@ -146,8 +143,6 @@
  private:
   class MovableObjectFixups;
 
-  explicit HeapCompact(ThreadHeap*);
-
   // Sample the amount of fragmentation and heap memory currently residing
   // on the freelists of the arenas we're able to compact. The computed
   // numbers will be subsequently used to determine if a heap compaction
diff --git a/third_party/blink/renderer/platform/heap/heap_compact_test.cc b/third_party/blink/renderer/platform/heap/heap_compact_test.cc
index 8fa8252..462218a 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact_test.cc
@@ -101,7 +101,7 @@
 
 TEST(HeapCompactTest, SparseBitmapBasic) {
   Address base = reinterpret_cast<Address>(0x10000u);
-  std::unique_ptr<SparseHeapBitmap> bitmap = SparseHeapBitmap::Create(base);
+  auto bitmap = std::make_unique<SparseHeapBitmap>(base);
 
   size_t double_chunk = 2 * kChunkRange;
 
@@ -134,7 +134,7 @@
 
 TEST(HeapCompactTest, SparseBitmapBuild) {
   Address base = reinterpret_cast<Address>(0x10000u);
-  std::unique_ptr<SparseHeapBitmap> bitmap = SparseHeapBitmap::Create(base);
+  auto bitmap = std::make_unique<SparseHeapBitmap>(base);
 
   size_t double_chunk = 2 * kChunkRange;
 
@@ -195,7 +195,7 @@
 
 TEST(HeapCompactTest, SparseBitmapLeftExtension) {
   Address base = reinterpret_cast<Address>(0x10000u);
-  std::unique_ptr<SparseHeapBitmap> bitmap = SparseHeapBitmap::Create(base);
+  auto bitmap = std::make_unique<SparseHeapBitmap>(base);
 
   SparseHeapBitmap* start = bitmap->HasRange(base, 1);
   EXPECT_TRUE(start);
@@ -211,7 +211,7 @@
             bitmap->HasRange(base - 2 * kUnitPointer, 1));
 
   // Reset.
-  bitmap = SparseHeapBitmap::Create(base);
+  bitmap = std::make_unique<SparseHeapBitmap>(base);
 
   // If attempting same as above, but the Address |A| is outside the
   // chunk size of a node, a new SparseHeapBitmap node needs to be
@@ -220,7 +220,7 @@
   EXPECT_NE(bitmap->HasRange(base, 1),
             bitmap->HasRange(base - 2 * kUnitPointer, 1));
 
-  bitmap = SparseHeapBitmap::Create(base);
+  bitmap = std::make_unique<SparseHeapBitmap>(base);
   bitmap->Add(base - kChunkRange + kUnitPointer);
   // This address is just inside the horizon and shouldn't create a new chunk.
   EXPECT_EQ(bitmap->HasRange(base, 1),
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
index bfafd73..54ce253 100644
--- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -112,8 +112,8 @@
     EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
     EXPECT_TRUE(not_fully_constructed_worklist_->IsGlobalEmpty());
     thread_state->EnableIncrementalMarkingBarrier();
-    thread_state->current_gc_data_.visitor =
-        MarkingVisitor::Create(thread_state, MarkingVisitor::kGlobalMarking);
+    thread_state->current_gc_data_.visitor = std::make_unique<MarkingVisitor>(
+        thread_state, MarkingVisitor::kGlobalMarking);
   }
 
   ~IncrementalMarkingScope() {
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.cc b/third_party/blink/renderer/platform/heap/marking_visitor.cc
index 25feea2c..51b2688 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.cc
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -17,11 +17,6 @@
 
 }  // namespace
 
-std::unique_ptr<MarkingVisitor> MarkingVisitor::Create(ThreadState* state,
-                                                       MarkingMode mode) {
-  return std::make_unique<MarkingVisitor>(state, mode);
-}
-
 MarkingVisitor::MarkingVisitor(ThreadState* state, MarkingMode marking_mode)
     : Visitor(state),
       marking_worklist_(Heap().GetMarkingWorklist(),
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.h b/third_party/blink/renderer/platform/heap/marking_visitor.h
index d7251a6..bd59f17 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.h
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.h
@@ -33,8 +33,6 @@
     kGlobalMarkingWithCompaction,
   };
 
-  static std::unique_ptr<MarkingVisitor> Create(ThreadState*, MarkingMode);
-
   // Write barrier that adds |value| to the set of marked objects. The barrier
   // bails out if marking is off or the object is not yet marked.
   ALWAYS_INLINE static void WriteBarrier(void* value);
diff --git a/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.cc b/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.cc
index 04793f31..b9ae01d 100644
--- a/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.cc
+++ b/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.cc
@@ -62,7 +62,7 @@
   // encompass.
   if (address >= MaxEnd()) {
     if (!right_) {
-      right_ = SparseHeapBitmap::Create(address);
+      right_ = std::make_unique<SparseHeapBitmap>(address);
       return;
     }
     right_->Add(address);
@@ -71,7 +71,7 @@
   // Same on the other side.
   if (address < MinStart()) {
     if (!left_) {
-      left_ = SparseHeapBitmap::Create(address);
+      left_ = std::make_unique<SparseHeapBitmap>(address);
       return;
     }
     left_->Add(address);
diff --git a/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h b/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h
index 70654e7..f16c2ca 100644
--- a/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h
+++ b/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h
@@ -40,10 +40,14 @@
 //
 class PLATFORM_EXPORT SparseHeapBitmap {
  public:
-  static std::unique_ptr<SparseHeapBitmap> Create(Address base) {
-    return base::WrapUnique(new SparseHeapBitmap(base));
+  explicit SparseHeapBitmap(Address base) : base_(base), size_(1) {
+    DCHECK(!(reinterpret_cast<uintptr_t>(base_) & kPointerAlignmentMask));
+    static_assert(kPointerAlignmentMask <= kAllocationMask,
+                  "address shift exceeds heap pointer alignment");
+    // For now, only recognize 8 and 4.
+    static_assert(alignof(void*) == 8 || alignof(void*) == 4,
+                  "unsupported pointer alignment");
   }
-
   ~SparseHeapBitmap() = default;
 
   // Return the sparse bitmap subtree that at least covers the
@@ -81,15 +85,6 @@
   size_t IntervalCount() const;
 
  private:
-  explicit SparseHeapBitmap(Address base) : base_(base), size_(1) {
-    DCHECK(!(reinterpret_cast<uintptr_t>(base_) & kPointerAlignmentMask));
-    static_assert(kPointerAlignmentMask <= kAllocationMask,
-                  "address shift exceeds heap pointer alignment");
-    // For now, only recognize 8 and 4.
-    static_assert(alignof(void*) == 8 || alignof(void*) == 4,
-                  "unsupported pointer alignment");
-  }
-
   Address Base() const { return base_; }
   size_t size() const { return size_; }
   Address end() const { return Base() + (size_ - 1); }
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index c5afac70..983ff58 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -1702,7 +1702,7 @@
           ? UnifiedHeapMarkingVisitor::Create(
                 this, GetMarkingMode(should_compact, take_snapshot),
                 GetIsolate())
-          : MarkingVisitor::Create(
+          : std::make_unique<MarkingVisitor>(
                 this, GetMarkingMode(should_compact, take_snapshot));
   current_gc_data_.stack_state = stack_state;
   current_gc_data_.marking_type = marking_type;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a051538..31427f58 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1397,6 +1397,10 @@
       status: "experimental",
     },
     {
+      name: "UnifiedPointerCaptureInBlink",
+      status: "stable",
+    },
+    {
       name: "UnifiedTouchAdjustment",
       status: "stable",
     },
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn
index d7d6dc0a..d6f3455 100644
--- a/third_party/blink/renderer/platform/scheduler/BUILD.gn
+++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -12,6 +12,7 @@
     "common/cancelable_closure_holder.cc",
     "common/cancelable_closure_holder.h",
     "common/cooperative_scheduling_manager.cc",
+    "common/event_loop.cc",
     "common/features.cc",
     "common/features.h",
     "common/frame_or_worker_scheduler.cc",
@@ -50,6 +51,8 @@
     "common/throttling/wake_up_budget_pool.h",
     "common/tracing_helper.cc",
     "common/tracing_helper.h",
+    "common/ukm_task_sampler.cc",
+    "common/ukm_task_sampler.h",
     "common/unprioritized_resource_loading_task_runner_handle.cc",
     "common/unprioritized_resource_loading_task_runner_handle.h",
     "common/web_resource_loading_task_runner_handle.cc",
@@ -103,6 +106,7 @@
     "main_thread/web_scoped_virtual_time_pauser.cc",
     "public/aggregated_metric_reporter.h",
     "public/cooperative_scheduling_manager.h",
+    "public/event_loop.h",
     "public/frame_or_worker_scheduler.h",
     "public/frame_scheduler.h",
     "public/frame_status.h",
@@ -149,6 +153,7 @@
     "//services/metrics/public/mojom",
     "//third_party/blink/renderer/platform:make_platform_generated",
     "//third_party/blink/renderer/platform/wtf",
+    "//v8",
   ]
 }
 
@@ -190,6 +195,7 @@
     "common/throttling/budget_pool_unittest.cc",
     "common/throttling/task_queue_throttler_unittest.cc",
     "common/tracing_helper_unittest.cc",
+    "common/ukm_task_sampler_unittest.cc",
     "common/worker_pool_unittest.cc",
     "main_thread/auto_advancing_virtual_time_domain_unittest.cc",
     "main_thread/deadline_task_runner_unittest.cc",
diff --git a/third_party/blink/renderer/platform/scheduler/common/event_loop.cc b/third_party/blink/renderer/platform/scheduler/common/event_loop.cc
new file mode 100644
index 0000000..b48398a
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/common/event_loop.cc
@@ -0,0 +1,68 @@
+// 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/scheduler/public/event_loop.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/trace_event/trace_event.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+namespace scheduler {
+
+// static
+scoped_refptr<EventLoop> EventLoop::CreateForWorkerOrWorklet(
+    v8::Isolate* isolate) {
+  return base::AdoptRef(new EventLoop(isolate));
+}
+
+EventLoop::EventLoop(v8::Isolate* isolate)
+    : isolate_(isolate), microtask_queue_(v8::MicrotaskQueue::New(isolate)) {
+  DCHECK(isolate_);
+}
+
+EventLoop::~EventLoop() {
+  microtask_queue_ = nullptr;
+
+  // TODO(tzik): Remove the instance from associated EventLoopGroup.
+}
+
+void EventLoop::EnqueueMicrotask(base::OnceClosure task) {
+  pending_microtasks_.push_back(std::move(task));
+  microtask_queue_->EnqueueMicrotask(isolate_, &EventLoop::RunPendingMicrotask,
+                                     this);
+}
+
+void EventLoop::PerformMicrotaskCheckpoint() {
+  microtask_queue_->PerformCheckpoint(isolate_);
+}
+
+// static
+void EventLoop::PerformIsolateGlobalMicrotasksCheckpoint(v8::Isolate* isolate) {
+  v8::MicrotasksScope::PerformCheckpoint(isolate);
+}
+
+void EventLoop::Disable() {
+  loop_enabled_ = false;
+  // TODO(tzik): Disable associated Frames.
+}
+
+void EventLoop::Enable() {
+  loop_enabled_ = true;
+  // TODO(tzik): Enable associated Frames.
+}
+
+// static
+void EventLoop::RunPendingMicrotask(void* data) {
+  TRACE_EVENT0("renderer.scheduler", "RunPendingMicrotask");
+  auto* self = static_cast<EventLoop*>(data);
+  base::OnceClosure task = std::move(self->pending_microtasks_.front());
+  self->pending_microtasks_.pop_front();
+  std::move(task).Run();
+}
+
+}  // namespace scheduler
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
index 2e01f50..6983216 100644
--- a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
@@ -11,18 +11,22 @@
 #include "base/time/default_tick_clock.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
+#include "third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler.h"
 
 namespace blink {
 namespace scheduler {
 
-using base::sequence_manager::TaskQueue;
 using base::sequence_manager::SequenceManager;
+using base::sequence_manager::TaskQueue;
 using base::sequence_manager::TaskTimeObserver;
 using base::sequence_manager::TimeDomain;
 
 SchedulerHelper::SchedulerHelper(
     std::unique_ptr<SequenceManager> sequence_manager)
-    : sequence_manager_(std::move(sequence_manager)), observer_(nullptr) {
+    : sequence_manager_(std::move(sequence_manager)),
+      observer_(nullptr),
+      ukm_task_sampler_(sequence_manager_->GetMetricRecordingSettings()
+                            .task_sampling_rate_for_recording_cpu_time) {
   sequence_manager_->SetWorkBatchSize(4);
 }
 
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
index 029d60e2..83fb455 100644
--- a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
@@ -14,6 +14,7 @@
 #include "base/time/tick_clock.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler.h"
 
 namespace blink {
 namespace scheduler {
@@ -99,8 +100,15 @@
   double GetSamplingRateForRecordingCPUTime() const;
   bool HasCPUTimingForEachTask() const;
 
+  bool ShouldRecordTaskUkm(bool task_has_thread_time) {
+    return ukm_task_sampler_.ShouldRecordTaskUkm(task_has_thread_time);
+  }
+
   // Test helpers.
   void SetWorkBatchSizeForTesting(int work_batch_size);
+  void SetUkmTaskSamplingRateForTest(double rate) {
+    ukm_task_sampler_.SetUkmTaskSamplingRate(rate);
+  }
 
  protected:
   void InitDefaultQueues(
@@ -118,6 +126,8 @@
 
   Observer* observer_;  // NOT OWNED
 
+  UkmTaskSampler ukm_task_sampler_;
+
   DISALLOW_COPY_AND_ASSIGN(SchedulerHelper);
 };
 
diff --git a/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.cc
index 218aa3a..8d3ec2f 100644
--- a/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.cc
@@ -9,44 +9,10 @@
 namespace blink {
 namespace scheduler {
 
-namespace {
-const double kSamplingRateForTaskUkm = 0.0001;
-}  // namespace
-
-ThreadSchedulerImpl::ThreadSchedulerImpl()
-    : ukm_task_sampling_rate_(kSamplingRateForTaskUkm),
-      uniform_distribution_(0.0f, 1.0f) {}
+ThreadSchedulerImpl::ThreadSchedulerImpl() {}
 
 ThreadSchedulerImpl::~ThreadSchedulerImpl() = default;
 
-bool ThreadSchedulerImpl::ShouldIgnoreTaskForUkm(bool has_thread_time,
-                                                 double* sampling_rate) {
-  const double thread_time_sampling_rate =
-      GetHelper()->GetSamplingRateForRecordingCPUTime();
-  if (thread_time_sampling_rate && *sampling_rate < thread_time_sampling_rate) {
-    if (!has_thread_time)
-      return true;
-    *sampling_rate /= thread_time_sampling_rate;
-  }
-  return false;
-}
-
-bool ThreadSchedulerImpl::ShouldRecordTaskUkm(bool has_thread_time) {
-  double sampling_rate = ukm_task_sampling_rate_;
-
-  // If thread_time is sampled as well, try to align UKM sampling with it so
-  // that we only record UKMs for tasks that also record thread_time.
-  if (ShouldIgnoreTaskForUkm(has_thread_time, &sampling_rate)) {
-    return false;
-  }
-
-  return uniform_distribution_(random_generator_) < sampling_rate;
-}
-
-void ThreadSchedulerImpl::SetUkmTaskSamplingRateForTest(double sampling_rate) {
-  ukm_task_sampling_rate_ = sampling_rate;
-}
-
 scoped_refptr<base::SingleThreadTaskRunner>
 ThreadSchedulerImpl::DeprecatedDefaultTaskRunner() {
   return DefaultTaskRunner();
diff --git a/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.h
index 5a14d5c3..8c99a9b6 100644
--- a/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.h
@@ -24,7 +24,6 @@
 
 namespace blink {
 namespace scheduler {
-class SchedulerHelper;
 
 // Scheduler-internal interface for the common methods between
 // MainThreadSchedulerImpl and NonMainThreadSchedulerImpl which should
@@ -58,24 +57,6 @@
  protected:
   ThreadSchedulerImpl();
   ~ThreadSchedulerImpl() override;
-
-  // Returns true if the current task should not be reported in UKM because no
-  // thread time was recorded for it. Also updates |sampling_rate| to account
-  // for the ignored tasks by sampling the remaining tasks with higher
-  // probability.
-  bool ShouldIgnoreTaskForUkm(bool has_thread_time, double* sampling_rate);
-
-  // Returns true with probability of kSamplingRateForTaskUkm.
-  bool ShouldRecordTaskUkm(bool has_thread_time);
-
-  virtual SchedulerHelper* GetHelper() = 0;
-
-  void SetUkmTaskSamplingRateForTest(double sampling_rate);
-
-  double ukm_task_sampling_rate_;
-
-  std::mt19937_64 random_generator_;
-  std::uniform_real_distribution<double> uniform_distribution_;
 };
 
 }  // namespace scheduler
diff --git a/third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler.cc b/third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler.cc
new file mode 100644
index 0000000..7e3fc68
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler.cc
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler.h"
+
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+namespace scheduler {
+
+UkmTaskSampler::UkmTaskSampler(double thread_time_sampling_rate,
+                               double ukm_task_sampling_rate)
+    : thread_time_sampling_rate_(clampTo(thread_time_sampling_rate, 0.0, 1.0)),
+      ukm_task_sampling_rate_(clampTo(ukm_task_sampling_rate, 0.0, 1.0)) {}
+
+double UkmTaskSampler::GetConditionalSamplingProbability(bool has_thread_time) {
+  if (thread_time_sampling_rate_ == 0.0 || ukm_task_sampling_rate_ == 0.0 ||
+      !(ukm_task_sampling_rate_ < 1.0)) {
+    return ukm_task_sampling_rate_;
+  }
+
+  if (thread_time_sampling_rate_ < ukm_task_sampling_rate_) {
+    if (has_thread_time) {
+      return 1.0;
+    } else {
+      // Note thread_time_sampling_rate_ < 1 given that
+      // thread_time_sampling_rate_ < ukm_task_sampling_rate_ < 1
+      return (ukm_task_sampling_rate_ - thread_time_sampling_rate_) /
+             (1.0 - thread_time_sampling_rate_);
+    }
+  } else {
+    if (has_thread_time) {
+      // Also covers the case when ukm_task_sampling_rate_ ==
+      // thread_time_sampling_rate_
+      return ukm_task_sampling_rate_ / thread_time_sampling_rate_;
+    } else {
+      return 0.0;
+    }
+  }
+}
+
+bool UkmTaskSampler::ShouldRecordTaskUkm(bool has_thread_time) {
+  double probability = GetConditionalSamplingProbability(has_thread_time);
+  std::bernoulli_distribution dist(probability);
+  return dist(random_generator_);
+}
+
+void UkmTaskSampler::SetUkmTaskSamplingRate(double rate) {
+  ukm_task_sampling_rate_ = clampTo(rate, 0.0, 1.0);
+}
+
+}  // namespace scheduler
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler.h b/third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler.h
new file mode 100644
index 0000000..40c95b3
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler.h
@@ -0,0 +1,56 @@
+// 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_SCHEDULER_COMMON_UKM_TASK_SAMPLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_UKM_TASK_SAMPLER_H_
+
+#include <random>
+
+#include "base/gtest_prod_util.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+namespace scheduler {
+
+// Helper to determine whether a task should be recorded for UKM. This class
+// tries to maximize the probability of recording an UKM sample for tasks that
+// are also recording their thread time.
+class PLATFORM_EXPORT UkmTaskSampler {
+ public:
+  static constexpr double kDefaultUkmTaskSamplingRate = 0.0001;
+
+  // Rates must be in the interval [0, 1] and will be clamped otherwise.
+  explicit UkmTaskSampler(
+      double thread_time_sampling_rate,
+      double ukm_task_sampling_rate = kDefaultUkmTaskSamplingRate);
+
+  // Returns true with probability of ukm_task_sampling_rate maximizing the
+  // probablility of recording UKMs for tasks that also record thread_time.
+  bool ShouldRecordTaskUkm(bool has_thread_time);
+
+  // |rate| must be in the interval [0, 1] and will be clamped otherwise.
+  void SetUkmTaskSamplingRate(double rate);
+
+ private:
+  // So that we can test GetConditionalSamplingProbability
+  FRIEND_TEST_ALL_PREFIXES(UkmTaskSamplerTest,
+                           GetConditionalSamplingProbability);
+  FRIEND_TEST_ALL_PREFIXES(UkmTaskSamplerTest,
+                           GetConditionalSamplingProbabilityWithEdgeCases);
+
+  // Returns the conditional probability [0, 1] of
+  // having to ukm sample given that has_thread_time has happened so that the
+  // actual probability of sampling is |ukm_task_sampling_rate_|
+  double GetConditionalSamplingProbability(bool has_thread_time);
+
+  double thread_time_sampling_rate_;
+  double ukm_task_sampling_rate_;
+
+  std::mt19937_64 random_generator_;
+};
+
+}  // namespace scheduler
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_UKM_TASK_SAMPLER_H_
diff --git a/third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler_unittest.cc
new file mode 100644
index 0000000..72b2b2e
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler_unittest.cc
@@ -0,0 +1,95 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/common/ukm_task_sampler.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/googletest/src/googlemock/include/gmock/gmock-matchers.h"
+
+namespace blink {
+namespace scheduler {
+
+using ::testing::DoubleEq;
+
+TEST(UkmTaskSamplerTest, SamplesAlwaysForProbabilityOne) {
+  UkmTaskSampler always_thread_time_sampler(
+      /*thread_time_sampling_rate = */ 1.0,
+      /*ukm_task_sampling_rate = */ 1.0);
+  UkmTaskSampler never_thread_time_sampler(
+      /*thread_time_sampling_rate = */ 0.0,
+      /*ukm_task_sampling_rate = */ 1.0);
+
+  for (int i = 0; i < 10; ++i) {
+    EXPECT_TRUE(always_thread_time_sampler.ShouldRecordTaskUkm(true));
+    EXPECT_TRUE(always_thread_time_sampler.ShouldRecordTaskUkm(false));
+    EXPECT_TRUE(never_thread_time_sampler.ShouldRecordTaskUkm(true));
+    EXPECT_TRUE(never_thread_time_sampler.ShouldRecordTaskUkm(false));
+  }
+}
+
+TEST(UkmTaskSamplerTest, NeverSamplesForProbabilityZero) {
+  UkmTaskSampler always_thread_time_sampler(
+      /*thread_time_sampling_rate = */ 1.0,
+      /*ukm_task_sampling_rate = */ 0.0);
+  UkmTaskSampler never_thread_time_sampler(
+      /*thread_time_sampling_rate = */ 0.0,
+      /*ukm_task_sampling_rate = */ 0.0);
+
+  for (int i = 0; i < 10; ++i) {
+    EXPECT_FALSE(always_thread_time_sampler.ShouldRecordTaskUkm(true));
+    EXPECT_FALSE(always_thread_time_sampler.ShouldRecordTaskUkm(false));
+    EXPECT_FALSE(never_thread_time_sampler.ShouldRecordTaskUkm(true));
+    EXPECT_FALSE(never_thread_time_sampler.ShouldRecordTaskUkm(false));
+  }
+}
+
+// Make sure that ukm_prob = ukm_prob_given_time * time_prob +
+// ukm_prob_given_no_time * no_time_prob
+TEST(UkmTaskSamplerTest, GetConditionalSamplingProbability) {
+  for (double time_prob = 0; time_prob < 1.0; time_prob += 0.1) {
+    UkmTaskSampler sampler(time_prob);
+    for (double expected_ukm_rate = 0; expected_ukm_rate < 1.0;
+         expected_ukm_rate += 0.1) {
+      sampler.SetUkmTaskSamplingRate(expected_ukm_rate);
+      double ukm_rate =
+          sampler.GetConditionalSamplingProbability(true) * time_prob +
+          sampler.GetConditionalSamplingProbability(false) * (1 - time_prob);
+      EXPECT_THAT(ukm_rate, DoubleEq(expected_ukm_rate))
+          << "For time_prob: " << time_prob;
+    }
+  }
+}
+
+TEST(UkmTaskSamplerTest, GetConditionalSamplingProbabilityWithEdgeCases) {
+  UkmTaskSampler sampler_0_0(/*thread_time_sampling_rate=*/0,
+                             /*ukm_task_sampling_rate=*/0);
+  EXPECT_EQ(sampler_0_0.GetConditionalSamplingProbability(false), 0.0);
+  // This doesn't really make sense given that thread_time_sampling_rate=0, but
+  // make sure we support it
+  EXPECT_EQ(sampler_0_0.GetConditionalSamplingProbability(true), 0.0);
+
+  UkmTaskSampler sampler_0_1(/*thread_time_sampling_rate=*/0,
+                             /*ukm_task_sampling_rate=*/1);
+  EXPECT_EQ(sampler_0_1.GetConditionalSamplingProbability(false), 1.0);
+  // This doesn't really make sense given that thread_time_sampling_rate=0, but
+  // make sure we support it
+  EXPECT_EQ(sampler_0_1.GetConditionalSamplingProbability(true), 1.0);
+
+  UkmTaskSampler sampler_1_0(/*thread_time_sampling_rate=*/1,
+                             /*ukm_task_sampling_rate=*/0);
+  EXPECT_EQ(sampler_1_0.GetConditionalSamplingProbability(true), 0.0);
+  // This doesn't really make sense given that thread_time_sampling_rate=1, but
+  // make sure we support it
+  EXPECT_EQ(sampler_1_0.GetConditionalSamplingProbability(false), 0.0);
+
+  UkmTaskSampler sampler_1_1(/*thread_time_sampling_rate=*/1,
+                             /*ukm_task_sampling_rate=*/1);
+  EXPECT_EQ(sampler_1_1.GetConditionalSamplingProbability(true), 1.0);
+  // This doesn't really make sense given that thread_time_sampling_rate=1, but
+  // make sure we support it
+  EXPECT_EQ(sampler_1_1.GetConditionalSamplingProbability(false), 1.0);
+}
+
+}  // namespace scheduler
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 46c3a38a..ae88ff6 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -1625,10 +1625,6 @@
   }
 }
 
-SchedulerHelper* MainThreadSchedulerImpl::GetHelper() {
-  return &helper_;
-}
-
 bool MainThreadSchedulerImpl::CanEnterLongIdlePeriod(
     base::TimeTicks now,
     base::TimeDelta* next_long_idle_period_delay_out) {
@@ -2353,7 +2349,7 @@
     MainThreadTaskQueue* queue,
     const base::sequence_manager::Task& task,
     const TaskQueue::TaskTiming& task_timing) {
-  if (!ShouldRecordTaskUkm(task_timing.has_thread_time()))
+  if (!helper_.ShouldRecordTaskUkm(task_timing.has_thread_time()))
     return;
 
   if (queue && queue->GetFrameScheduler()) {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
index d277cd1..c94ef13 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -562,9 +562,6 @@
     MainThreadSchedulerImpl* scheduler_;  // NOT OWNED
   };
 
-  // ThreadSchedulerImpl implementation:
-  SchedulerHelper* GetHelper() override;
-
   // IdleHelper::Delegate implementation:
   bool CanEnterLongIdlePeriod(
       base::TimeTicks now,
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
index 28cf444a..d75c6de 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -3407,27 +3407,6 @@
   EXPECT_EQ(base::Time::Now(), base::Time::FromJsTime(1000000.0));
 }
 
-TEST_F(MainThreadSchedulerImplTest, ShouldIgnoreTaskForUkm) {
-  bool supports_thread_ticks = base::ThreadTicks::IsSupported();
-  double sampling_rate;
-
-  sampling_rate = 0.0001;
-  EXPECT_FALSE(scheduler_->ShouldIgnoreTaskForUkm(true, &sampling_rate));
-  if (supports_thread_ticks) {
-    EXPECT_EQ(0.01, sampling_rate);
-  } else {
-    EXPECT_EQ(0.0001, sampling_rate);
-  }
-
-  sampling_rate = 0.0001;
-  if (supports_thread_ticks) {
-    EXPECT_TRUE(scheduler_->ShouldIgnoreTaskForUkm(false, &sampling_rate));
-  } else {
-    EXPECT_FALSE(scheduler_->ShouldIgnoreTaskForUkm(false, &sampling_rate));
-    EXPECT_EQ(0.0001, sampling_rate);
-  }
-}
-
 class CompositingExperimentWithExplicitSignalsTest
     : public MainThreadSchedulerImplTest {
  public:
diff --git a/third_party/blink/renderer/platform/scheduler/public/event_loop.h b/third_party/blink/renderer/platform/scheduler/public/event_loop.h
new file mode 100644
index 0000000..68b4bd2
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/public/event_loop.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_EVENT_LOOP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_EVENT_LOOP_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace v8 {
+class Isolate;
+class MicrotaskQueue;
+}  // namespace v8
+
+namespace blink {
+namespace scheduler {
+
+// TODO(tzik): Implement EventLoopGroup that represents a group of reachable
+// browsing contexts.
+
+// Represents an event loop. The instance is held by ExecutionContexts.
+// https://html.spec.whatwg.org/multipage/webappapis.html#event-loop
+//
+// Browsing contexts must share the same EventLoop if they have a chance to
+// access each other synchronously.
+// That is:
+//  - Two Documents must share the same EventLoop if they are scriptable with
+//    each other.
+//  - Workers and Worklets can have its own EventLoop, as no other browsing
+//    context can access it synchronously.
+class PLATFORM_EXPORT EventLoop final : public WTF::RefCounted<EventLoop> {
+  USING_FAST_MALLOC(EventLoop);
+
+ public:
+  // An static constructor for Workers and Worklets.
+  // For Document, use EventLoopGroup to get or create the instance.
+  static scoped_refptr<EventLoop> CreateForWorkerOrWorklet(
+      v8::Isolate* isolate);
+
+  // Queues |cb| to the backing v8::MicrotaskQueue.
+  void EnqueueMicrotask(base::OnceClosure cb);
+
+  // Runs pending microtasks until the queue is empty.
+  void PerformMicrotaskCheckpoint();
+
+  // Runs pending microtasks on the isolate's default MicrotaskQueue until it's
+  // empty.
+  static void PerformIsolateGlobalMicrotasksCheckpoint(v8::Isolate* isolate);
+
+  // Disables or enables all controlled frames.
+  void Disable();
+  void Enable();
+
+  // Returns the MicrotaskQueue instance to be associated to v8::Context. Pass
+  // it to v8::Context::New().
+  v8::MicrotaskQueue* microtask_queue() const { return microtask_queue_.get(); }
+
+ private:
+  friend class WTF::RefCounted<EventLoop>;
+
+  explicit EventLoop(v8::Isolate* isolate);
+  ~EventLoop();
+
+  static void RunPendingMicrotask(void* data);
+
+  // TODO(tzik): Add a back pointer to EventLoopGroup.
+
+  v8::Isolate* isolate_;
+  bool loop_enabled_ = true;
+  Deque<base::OnceClosure> pending_microtasks_;
+  std::unique_ptr<v8::MicrotaskQueue> microtask_queue_;
+
+  DISALLOW_COPY_AND_ASSIGN(EventLoop);
+};
+
+}  // namespace scheduler
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_EVENT_LOOP_H_
diff --git a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.cc
index def26167..ec6f870d 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.cc
@@ -106,9 +106,5 @@
   return helper_.GetClock();
 }
 
-SchedulerHelper* NonMainThreadSchedulerImpl::GetHelper() {
-  return &helper_;
-}
-
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.h
index 1edbe7c..c83092c 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.h
@@ -100,8 +100,6 @@
   NonMainThreadSchedulerHelper* helper() { return &helper_; }
 
  private:
-  SchedulerHelper* GetHelper() override;
-
   NonMainThreadSchedulerHelper helper_;
 
   DISALLOW_COPY_AND_ASSIGN(NonMainThreadSchedulerImpl);
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
index 20fda05..c177a90 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
@@ -307,7 +307,7 @@
     NonMainThreadTaskQueue* worker_task_queue,
     const base::sequence_manager::Task& task,
     const base::sequence_manager::TaskQueue::TaskTiming& task_timing) {
-  if (!ShouldRecordTaskUkm(task_timing.has_thread_time()))
+  if (!helper()->ShouldRecordTaskUkm(task_timing.has_thread_time()))
     return;
   ukm::builders::RendererSchedulerTask builder(ukm_source_id_);
 
@@ -331,6 +331,10 @@
   ukm_recorder_ = std::move(ukm_recorder);
 }
 
+void WorkerThreadScheduler::SetUkmTaskSamplingRateForTest(double rate) {
+  helper()->SetUkmTaskSamplingRateForTest(rate);
+}
+
 void WorkerThreadScheduler::SetCPUTimeBudgetPoolForTesting(
     CPUTimeBudgetPool* cpu_time_budget_pool) {
   cpu_time_budget_pool_ = cpu_time_budget_pool;
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
index a1af776..88b6931 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
@@ -127,6 +127,7 @@
 
   std::unordered_set<WorkerScheduler*>& GetWorkerSchedulersForTesting();
 
+  void SetUkmTaskSamplingRateForTest(double rate);
   void SetUkmRecorderForTest(std::unique_ptr<ukm::UkmRecorder> ukm_recorder);
 
  private:
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
index 0c49780..0428317 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
@@ -76,8 +76,8 @@
         clock_(clock_),
         timeline_(timeline) {}
 
-  using ThreadSchedulerImpl::SetUkmTaskSamplingRateForTest;
   using WorkerThreadScheduler::SetUkmRecorderForTest;
+  using WorkerThreadScheduler::SetUkmTaskSamplingRateForTest;
 
  private:
   bool CanEnterLongIdlePeriod(
diff --git a/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h b/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h
index 9a9e268..e91bc24 100644
--- a/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h
+++ b/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h
@@ -34,6 +34,8 @@
   state.output_clip = output_clip;
   state.opacity = opacity;
   state.direct_compositing_reasons = compositing_reasons;
+  state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
+      NewUniqueObjectId(), CompositorElementIdNamespace::kPrimary);
   return EffectPaintPropertyNode::Create(parent, std::move(state));
 }
 
@@ -46,17 +48,17 @@
                              compositing_reasons);
 }
 
-inline scoped_refptr<EffectPaintPropertyNode>
-CreateCompositedAnimatingOpacityEffect(
+inline scoped_refptr<EffectPaintPropertyNode> CreateAnimatingOpacityEffect(
     const EffectPaintPropertyNode& parent,
-    float opacity,
+    float opacity = 1.f,
     const ClipPaintPropertyNode* output_clip = nullptr) {
   EffectPaintPropertyNode::State state;
   state.local_transform_space = &parent.Unalias().LocalTransformSpace();
   state.output_clip = output_clip;
   state.opacity = opacity;
   state.direct_compositing_reasons = CompositingReason::kActiveOpacityAnimation;
-  state.is_running_opacity_animation_on_compositor = true;
+  state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
+      NewUniqueObjectId(), CompositorElementIdNamespace::kPrimaryEffect);
   return EffectPaintPropertyNode::Create(parent, std::move(state));
 }
 
@@ -73,6 +75,8 @@
   state.filter = std::move(filter);
   state.filters_origin = filters_origin;
   state.direct_compositing_reasons = compositing_reasons;
+  state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
+      NewUniqueObjectId(), CompositorElementIdNamespace::kEffectFilter);
   return EffectPaintPropertyNode::Create(parent, std::move(state));
 }
 
@@ -86,6 +90,20 @@
                             compositing_reasons);
 }
 
+inline scoped_refptr<EffectPaintPropertyNode> CreateAnimatingFilterEffect(
+    const EffectPaintPropertyNode& parent,
+    CompositorFilterOperations filter = CompositorFilterOperations(),
+    const ClipPaintPropertyNode* output_clip = nullptr) {
+  EffectPaintPropertyNode::State state;
+  state.local_transform_space = &parent.Unalias().LocalTransformSpace();
+  state.output_clip = output_clip;
+  state.filter = std::move(filter);
+  state.direct_compositing_reasons = CompositingReason::kActiveFilterAnimation;
+  state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
+      NewUniqueObjectId(), CompositorElementIdNamespace::kEffectFilter);
+  return EffectPaintPropertyNode::Create(parent, std::move(state));
+}
+
 inline scoped_refptr<EffectPaintPropertyNode> CreateBackdropFilterEffect(
     const EffectPaintPropertyNode& parent,
     const TransformPaintPropertyNode& local_transform_space,
@@ -99,6 +117,8 @@
   state.backdrop_filter = std::move(backdrop_filter);
   state.filters_origin = filters_origin;
   state.direct_compositing_reasons = compositing_reasons;
+  state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
+      NewUniqueObjectId(), CompositorElementIdNamespace::kPrimary);
   return EffectPaintPropertyNode::Create(parent, std::move(state));
 }
 
@@ -113,6 +133,22 @@
       compositing_reasons);
 }
 
+inline scoped_refptr<EffectPaintPropertyNode>
+CreateAnimatingBackdropFilterEffect(
+    const EffectPaintPropertyNode& parent,
+    CompositorFilterOperations backdrop_filter = CompositorFilterOperations(),
+    const ClipPaintPropertyNode* output_clip = nullptr) {
+  EffectPaintPropertyNode::State state;
+  state.local_transform_space = &parent.Unalias().LocalTransformSpace();
+  state.output_clip = output_clip;
+  state.backdrop_filter = std::move(backdrop_filter);
+  state.direct_compositing_reasons =
+      CompositingReason::kActiveBackdropFilterAnimation;
+  state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
+      NewUniqueObjectId(), CompositorElementIdNamespace::kPrimaryEffect);
+  return EffectPaintPropertyNode::Create(parent, std::move(state));
+}
+
 inline scoped_refptr<ClipPaintPropertyNode> CreateClip(
     const ClipPaintPropertyNode& parent,
     const TransformPaintPropertyNode& local_transform_space,
@@ -155,6 +191,19 @@
   return TransformPaintPropertyNode::Create(parent, std::move(state));
 }
 
+inline scoped_refptr<TransformPaintPropertyNode> CreateAnimatingTransform(
+    const TransformPaintPropertyNode& parent,
+    const TransformationMatrix& matrix = TransformationMatrix(),
+    const FloatPoint3D& origin = FloatPoint3D()) {
+  TransformPaintPropertyNode::State state{
+      TransformPaintPropertyNode::TransformAndOrigin(matrix, origin)};
+  state.direct_compositing_reasons =
+      CompositingReason::kActiveTransformAnimation;
+  state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
+      NewUniqueObjectId(), CompositorElementIdNamespace::kPrimaryTransform);
+  return TransformPaintPropertyNode::Create(parent, std::move(state));
+}
+
 inline scoped_refptr<TransformPaintPropertyNode> CreateScrollTranslation(
     const TransformPaintPropertyNode& parent,
     float offset_x,
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 fd87285f0..7030038 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
@@ -40,6 +40,7 @@
             'base::MakeRefCounted',
             'base::Optional',
             'base::OptionalOrNullptr',
+            'base::PlatformThread',
             'base::PlatformThreadId',
             'base::RefCountedData',
             'base::RunLoop',
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 2ee56c4..a602f09 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -98,7 +98,6 @@
 crbug.com/591099 css3/filters/effect-reference.html [ Failure ]
 crbug.com/591099 css3/filters/filter-repaint-composited-fallback-crash.html [ Pass ]
 crbug.com/591099 css3/filters/filter-repaint-composited-fallback.html [ Pass ]
-crbug.com/591099 css3/flexbox/intrinsic-width-orthogonal-writing-mode.html [ Failure Pass ]
 crbug.com/591099 editing/selection/paint-hyphen.html [ Pass ]
 crbug.com/591099 external/wpt/WebCryptoAPI/derive_bits_keys/pbkdf2.https.any.html?1001-2000 [ Pass ]
 crbug.com/591099 external/wpt/WebCryptoAPI/derive_bits_keys/pbkdf2.https.any.html?2001-3000 [ Pass ]
@@ -134,7 +133,7 @@
 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-animations/Element-getAnimations-dynamic-changes.tentative.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-animations/Element-getAnimations.tentative.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-animations/Element-getAnimations.tentative.html [ Failure Pass ]
 crbug.com/591099 external/wpt/css/css-contain/contain-size-grid-002.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-contain/contain-size-multicol-001.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-contain/contain-style-counters-004.html [ Failure ]
@@ -221,7 +220,7 @@
 crbug.com/40634 external/wpt/css/css-text/white-space/trailing-space-before-br-001.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-transforms/transform-box/view-box-mutation.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-transitions/properties-value-003.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css/css-transitions/properties-value-003.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-ui/text-overflow-010.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-ui/text-overflow-026.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-values/attr-invalid-type-008.html [ Failure ]
@@ -374,7 +373,7 @@
 crbug.com/591099 external/wpt/fetch/http-cache/cc-request.html [ Pass ]
 crbug.com/591099 external/wpt/fullscreen/api/element-ready-check-containing-iframe-manual.html [ Pass ]
 crbug.com/591099 external/wpt/fullscreen/api/element-request-fullscreen-and-remove-manual.html [ Failure Pass ]
-crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure Pass ]
+crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure ]
 crbug.com/591099 external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html [ Crash ]
 crbug.com/591099 external/wpt/html/browsers/the-window-object/window-open-noopener.html?_parent [ Pass ]
 crbug.com/591099 external/wpt/html/browsers/the-window-object/window-open-noopener.html?_top [ Pass ]
@@ -405,7 +404,7 @@
 crbug.com/591099 external/wpt/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Pass ]
 crbug.com/591099 external/wpt/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Pass ]
 crbug.com/591099 external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
-crbug.com/591099 external/wpt/screen-orientation/onchange-event.html [ Failure Timeout ]
+crbug.com/591099 external/wpt/screen-orientation/onchange-event.html [ Timeout ]
 crbug.com/591099 external/wpt/service-workers/service-worker/fetch-frame-resource.https.html [ Pass ]
 crbug.com/591099 external/wpt/shadow-dom/untriaged/html-elements-in-shadow-trees/html-forms/test-003.html [ Failure Pass ]
 crbug.com/591099 external/wpt/svg/text/reftests/text-inline-size-101.svg [ Failure ]
@@ -456,7 +455,7 @@
 crbug.com/591099 fast/dom/SelectorAPI/resig-SelectorsAPI-test.xhtml [ Pass ]
 crbug.com/591099 fast/dom/shadow/focus-controller-recursion-crash.html [ Pass ]
 crbug.com/591099 fast/dom/shadow/svg-style-in-shadow-tree-crash.html [ Pass ]
-crbug.com/591099 fast/events/before-unload-return-value-from-listener.html [ Pass ]
+crbug.com/591099 fast/events/before-unload-return-value-from-listener.html [ Pass Timeout ]
 crbug.com/591099 fast/events/touch/compositor-touch-hit-rects-continuation.html [ Failure ]
 crbug.com/591099 fast/events/touch/compositor-touch-hit-rects-list-translate.html [ Failure ]
 crbug.com/591099 fast/events/touch/compositor-touch-hit-rects.html [ Failure ]
@@ -523,7 +522,7 @@
 crbug.com/591099 storage/indexeddb/objectstore-cursor.html [ Pass ]
 crbug.com/591099 storage/indexeddb/structured-clone.html [ Pass ]
 crbug.com/591099 storage/websql/open-database-creation-callback-isolated-world.html [ Pass ]
-crbug.com/591099 storage/websql/transaction-error-callback.html [ Pass ]
+crbug.com/591099 storage/websql/transaction-error-callback.html [ Pass Timeout ]
 crbug.com/591099 svg/animations/svg-animation-policy-once.html [ Pass ]
 crbug.com/591099 svg/zoom/page/zoom-svg-float-border-padding.xml [ Pass ]
 crbug.com/591099 tables/mozilla/bugs/bug14159-1.html [ Pass ]
@@ -531,13 +530,17 @@
 crbug.com/591099 virtual/android/rootscroller/set-root-scroller.html [ Pass ]
 crbug.com/591099 virtual/android/rootscroller/set-rootscroller-before-load.html [ Pass ]
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/modify_move/move-forward-after-line-break.html [ Failure ]
-crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_23_ltr.html [ Failure ]
+crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_23_ltr.html [ Failure Pass ]
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_05_ltr_multi_line.html [ Failure ]
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_09_ltr_multi_line.html [ Failure ]
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_09_rtl_multi_line.html [ Failure ]
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_01_rtl_multi_line.html [ Failure ]
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_05_rtl_multi_line.html [ Failure ]
 crbug.com/591099 virtual/binary-for-devtools/http/tests/devtools/elements/highlight/highlight-css-grid.js [ Failure ]
+crbug.com/591099 virtual/binary-for-devtools/http/tests/devtools/sources/debugger-frameworks/frameworks-jquery.js [ Crash Pass ]
+crbug.com/591099 virtual/binary-for-inspector-protocol/http/tests/inspector-protocol/fetch/fetch-basic.js [ Pass Timeout ]
+crbug.com/591099 virtual/binary-for-inspector-protocol/http/tests/inspector-protocol/fetch/fetch-renderer.js [ Pass Timeout ]
+crbug.com/591099 virtual/binary-for-inspector-protocol/http/tests/inspector-protocol/network/interception-multiclient.js [ Pass ]
 crbug.com/916511 virtual/composite-after-paint/paint/background/scrolling-background-with-negative-z-child.html [ Crash ]
 crbug.com/591099 virtual/composite-after-paint/paint/invalidation/box/margin.html [ Failure Pass ]
 Bug(none) virtual/disable-blink-gen-property-trees/ [ Skip ]
@@ -554,7 +557,7 @@
 crbug.com/591099 virtual/fractional_scrolling/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Failure ]
 crbug.com/591099 virtual/gpu-rasterization/images/color-profile-image-filter-all.html [ Pass ]
 crbug.com/591099 virtual/gpu-rasterization/images/image-page-injected-script-crash.html [ Pass ]
-crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Pass ]
+crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Failure Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-filter.html [ Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-arc-circumference-fill.html [ Failure ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-blend-image.html [ Pass ]
@@ -604,5 +607,5 @@
 crbug.com/591099 virtual/video-surface-layer/media/video-controls-hide-on-move-outside-controls.html [ Pass ]
 crbug.com/591099 virtual/video-surface-layer/media/video-played-ranges-1.html [ Pass ]
 crbug.com/591099 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCIceTransport-extension.https.html [ Failure Pass ]
-crbug.com/591099 vr/getFrameData_oneframeupdate.html [ Failure Pass ]
+crbug.com/591099 vr/getFrameData_oneframeupdate.html [ Pass ]
 crbug.com/591099 webexposed/global-interface-listing-shared-worker.html [ Pass ]
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index ef83e7bd..167d9d3 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -419,6 +419,9 @@
 crbug.com/865262 [ Mac ] media/controls/text-track-menu-pointer-selection.html [ Slow ]
 crbug.com/876050 [ Mac ] virtual/video-surface-layer/media/controls/text-track-menu-pointer-selection.html [ Slow ]
 
+crbug.com/942951 media/controls/controls-layout-in-different-size.html [ Slow ]
+crbug.com/942951 virtual/video-surface-layer/media/controls/controls-layout-in-different-size.html [ Slow ]
+
 crbug.com/910627 webexposed/global-interface-listing-shared-worker.html [ Slow ]
 crbug.com/910627 virtual/stable/webexposed/global-interface-listing-shared-worker.html [ Slow ]
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 1ba0d0e..675cbea 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -257,6 +257,7 @@
 # Subpixel differences
 crbug.com/771643 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-color-5.html [ Failure ]
 crbug.com/771643 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-image-5.html [ Failure ]
+crbug.com/836884 external/wpt/css/filter-effects/css-filters-animation-opacity.html [ Failure ]
 
 # Fails due to flaws in the SPv1 architecture. Can be fixed with composite-after-paint.
 crbug.com/862483 compositing/overflow/scrollbar-layer-placement-negative-z-index-child.html [ Failure ]
@@ -1777,10 +1778,6 @@
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_into_inline_block_multiline.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_into_inline_block_nested.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_into_inline_block_one_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_23_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_23_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_24_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_24_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_01_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_01_rtl_multi_line.html [ Failure Crash ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_02_ltr_multi_line.html [ Failure ]
@@ -1799,10 +1796,6 @@
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_08_rtl_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_09_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_09_rtl_multi_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_23_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_23_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_24_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_24_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_01_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_01_rtl_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_02_ltr_multi_line.html [ Failure Crash ]
@@ -4636,8 +4629,6 @@
 crbug.com/731535 [ Mac ] virtual/user-activation-v2/fast/dom/Window/window-resize-contents.html [ Failure Pass ]
 crbug.com/731535 [ Win7 ] virtual/user-activation-v2/fast/dom/Window/window-resize-contents.html [ Failure Pass ]
 
-crbug.com/732103 [ Mac ] http/tests/shapedetection/shapedetection-cross-origin.html [ Timeout Pass ]
-
 # Sheriff failures 2017-06-14
 crbug.com/737959 http/tests/misc/object-image-load-outlives-gc-without-crashing.html [ Failure Pass ]
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 97c5dee..8963d3e 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -127,6 +127,12 @@
   },
   {
     "prefix": "disable-blink-gen-property-trees",
+    "base": "transitions",
+    "args": ["--disable-blink-features=BlinkGenPropertyTrees",
+             "--enable-threaded-compositing"]
+  },
+  {
+    "prefix": "disable-blink-gen-property-trees",
     "base": "compositing",
     "args": ["--disable-blink-features=BlinkGenPropertyTrees"]
   },
diff --git a/third_party/blink/web_tests/custom-elements/form-submission-file.html b/third_party/blink/web_tests/custom-elements/form-submission-file.html
index a5cdae6..4beb875 100644
--- a/third_party/blink/web_tests/custom-elements/form-submission-file.html
+++ b/third_party/blink/web_tests/custom-elements/form-submission-file.html
@@ -29,7 +29,7 @@
     for (let p of nameValues) {
       formData.append(p[0], p[1]);
     }
-    this.internals_.setFormValue(this.value_, formData);
+    this.internals_.setFormValue(formData);
   }
 }
 customElements.define('my-control', MyControl);
diff --git a/third_party/blink/web_tests/custom-elements/resources/form-associated-state-restore-frame.html b/third_party/blink/web_tests/custom-elements/resources/form-associated-state-restore-frame.html
index fbfaf9a..5f4eb46b 100644
--- a/third_party/blink/web_tests/custom-elements/resources/form-associated-state-restore-frame.html
+++ b/third_party/blink/web_tests/custom-elements/resources/form-associated-state-restore-frame.html
@@ -15,7 +15,7 @@
     this.value_ = v;
     this.internals_.setFormValue(v);
   }
-  restoreValueCallback(v, mode) {
+  restoreStateCallback(v, mode) {
     this.value = v;
     this.lastRestoreMode = mode;
   }
@@ -48,7 +48,7 @@
       this.value_ = v;
       this.internals_.setFormValue(v);
     }
-    restoreValueCallback(v, mode) {
+    restoreStateCallback(v, mode) {
       this.value = v;
       this.lastRestoreMode = mode;
     }
diff --git a/third_party/blink/web_tests/custom-elements/tentative/form-submission.html b/third_party/blink/web_tests/custom-elements/tentative/form-submission.html
index e99e0c3e..56db047 100644
--- a/third_party/blink/web_tests/custom-elements/tentative/form-submission.html
+++ b/third_party/blink/web_tests/custom-elements/tentative/form-submission.html
@@ -24,7 +24,7 @@
     for (let p of nameValues) {
       formData.append(p[0], p[1]);
     }
-    this.internals_.setFormValue(this.value_, formData);
+    this.internals_.setFormValue(formData);
   }
 }
 customElements.define('my-control', MyControl);
@@ -96,6 +96,7 @@
       '</form>' +
       '<iframe name="if1"></iframe>';
   $('my-control').value = 'value-ce1';
+  $('my-control').setValues([]);
   $('my-control').setValues([['sub1', 'subvalue1'],
                              ['sub2', 'subvalue2'],
                              ['sub2', 'subvalue3']]);
diff --git a/third_party/blink/web_tests/editing/execCommand/infinite-recursion-computeRectForRepaint-expected.txt b/third_party/blink/web_tests/editing/execCommand/infinite-recursion-computeRectForRepaint-expected.txt
deleted file mode 100644
index fd40910..0000000
--- a/third_party/blink/web_tests/editing/execCommand/infinite-recursion-computeRectForRepaint-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/third_party/blink/web_tests/editing/execCommand/infinite-recursion-computeRectForRepaint.html b/third_party/blink/web_tests/editing/execCommand/infinite-recursion-computeRectForRepaint.html
index bba07f7..b5d1e87 100644
--- a/third_party/blink/web_tests/editing/execCommand/infinite-recursion-computeRectForRepaint.html
+++ b/third_party/blink/web_tests/editing/execCommand/infinite-recursion-computeRectForRepaint.html
@@ -1,7 +1,7 @@
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
-    if (window.testRunner)
-        testRunner.dumpAsText();
-
     function go() {
         document.execCommand("selectall", false);
         document.designMode="on";
@@ -41,5 +41,5 @@
         document.execCommand("insertorderedlist");
     }
 </script>
-<body onload="go()">
+<body onload="test(go)">
 </body>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_23_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_23_rtl.html
index 60271c0..7c375636 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_23_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_23_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|Lorem\n        <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsum|</div>'
+    : '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>',
   '23-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">|Lorem\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   '23-1 rtl left character');
 
 selection_test(
@@ -36,133 +42,180 @@
 selection_test(
   '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   '23-5 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n|        <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   '23-6 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n |       <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   '23-7 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n  |      <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   '23-8 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n   |     <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   '23-9 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n    |    <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   '23-10 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n     |   <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   '23-11 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n      |  <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   '23-12 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n       | <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   '23-13 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        |<div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   '23-14 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div>|</div>\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
+  usesBidiAffinity
+    // The input position is canonicalized into "|ipsum", which causes the
+    // current behavior. It might be changed in the future.
+    ? '<div contenteditable dir="rtl">Lorem\n        <div>|</div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
   '23-15 rtl left character');
 
+
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>|\n        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>|\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
   '23-16 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n|        ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n|        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
   '23-17 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n |       ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n |       ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
   '23-18 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n  |      ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n  |      ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
   '23-19 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n   |     ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n   |     ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
   '23-20 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n    |    ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n    |    ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
   '23-21 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n     |   ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n     |   ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
   '23-22 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n      |  ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n      |  ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
   '23-23 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n       | ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n       | ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
   '23-24 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
   '23-25 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsum|</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsum|</div>',
   '23-26 rtl left character');
 
 selection_test(
@@ -186,6 +239,8 @@
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsum|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsum|</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsum|</div>',
   '23-30 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_24_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_24_ltr.html
index d0d7cbb9..fd44320 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_24_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_24_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">|\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-0 ltr left character');
 
 selection_test(
@@ -24,133 +28,179 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">|\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-3 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-4 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n|        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n|        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-5 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n |       <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n |       <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-6 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n  |      <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n  |      <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-7 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n   |     <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n   |     <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-8 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n    |    <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n    |    <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-9 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n     |   <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n     |   <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-10 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n      |  <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n      |  <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-11 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n       | <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n       | <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-12 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        |<div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        |<div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-13 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div>|</div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    // The input position is canonicalized into "|\u05DE\u05E6\u05E0\u05E4\u05EA",
+    // which causes the current behavior. It might be changed in the future.
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-14 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>|\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-15 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n|        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-16 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n |       \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-17 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n  |      \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-18 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n   |     \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-19 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n    |    \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-20 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n     |   \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-21 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n      |  \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-22 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n       | \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-23 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-24 ltr left character');
 
 selection_test(
@@ -174,12 +224,16 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA|</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-28 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">|\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>',
   '24-29 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_page_down_inline_block_hang.html b/third_party/blink/web_tests/editing/selection/modify_move/move_page_down_inline_block_hang.html
new file mode 100644
index 0000000..2f6f7b1
--- /dev/null
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_page_down_inline_block_hang.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<div contenteditable="true" style="display: inline-block;" id=target>
+<div style="display: inline-block;">test<br><br></div>
+</div>
+<script>
+// Regression test for crbug.com/933839
+test(() => {
+  assert_own_property(
+      window, 'testRunner',
+      'This test requires testRunner to perform PageDown selection movement');
+  target.focus();
+  testRunner.execCommand('MovePageDown');
+}, 'PageDown with nested inline blocks does not hang');
+</script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_23_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_23_rtl.html
index 73ff4a4..1665e0d 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_23_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_23_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|Lorem\n        <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|Lorem\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">|Lorem\n        <div></div>\n        ipsum</div>',
   '23-0 rtl right character');
 
 selection_test(
@@ -30,133 +34,179 @@
 selection_test(
   '<div contenteditable dir="rtl">Lore|m\n        <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|Lorem\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">|Lorem\n        <div></div>\n        ipsum</div>',
   '23-4 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
   '23-5 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n|        <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n|        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
   '23-6 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n |       <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n |       <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
   '23-7 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n  |      <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n  |      <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
   '23-8 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n   |     <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n   |     <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
   '23-9 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n    |    <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n    |    <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
   '23-10 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n     |   <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n     |   <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
   '23-11 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n      |  <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n      |  <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
   '23-12 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n       | <div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n       | <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
   '23-13 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        |<div></div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        |<div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">L|orem\n        <div></div>\n        ipsum</div>',
   '23-14 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div>|</div>\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    // The input position is canonicalized into "|ipsum", which causes the
+    // current behavior. It might be changed in the future.
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>'
+    : '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   '23-15 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>|\n        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>'
+    : '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   '23-16 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n|        ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>'
+    : '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   '23-17 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n |       ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>'
+    : '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   '23-18 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n  |      ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>'
+    : '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   '23-19 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n   |     ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>'
+    : '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   '23-20 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n    |    ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>'
+    : '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   '23-21 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n     |   ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>'
+    : '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   '23-22 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n      |  ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>'
+    : '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   '23-23 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n       | ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>'
+    : '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   '23-24 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>'
+    : '<div contenteditable dir="rtl">Lorem|\n        <div></div>\n        ipsum</div>',
   '23-25 rtl right character');
 
 selection_test(
@@ -180,12 +230,16 @@
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsu|m</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsum|</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        |ipsum</div>',
   '23-29 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        ipsum|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="rtl">|Lorem\n        <div></div>\n        ipsum</div>'
+    : '<div contenteditable dir="rtl">Lorem\n        <div></div>\n        i|psum</div>',
   '23-30 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_24_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_24_ltr.html
index e4e5d4e1..bf349bb 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_24_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_24_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA|</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6|\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">|\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-1 ltr right character');
 
 selection_test(
@@ -30,133 +36,179 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA|\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-4 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n|        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-5 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n |       <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-6 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n  |      <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-7 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n   |     <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-8 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n    |    <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-9 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n     |   <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-10 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n      |  <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-11 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n       | <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-12 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        |<div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7|\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   '24-13 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div>|</div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
+  usesBidiAffinity
+    // The input position is canonicalized into "|\u05DE\u05E6\u05E0\u05E4\u05EA",
+    // which causes the current behavior. It might be changed in the future.
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div>|</div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
   '24-14 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>|\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>|\n        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
   '24-15 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n|        \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n|        \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
   '24-16 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n |       \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n |       \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
   '24-17 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n  |      \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n  |      \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
   '24-18 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n   |     \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n   |     \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
   '24-19 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n    |    \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n    |    \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
   '24-20 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n     |   \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n     |   \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
   '24-21 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n      |  \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n      |  \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
   '24-22 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n       | \u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n       | \u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
   '24-23 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>',
   '24-24 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE|\u05E6\u05E0\u05E4\u05EA</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA|</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        |\u05DE\u05E6\u05E0\u05E4\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA|</div>',
   '24-25 ltr right character');
 
 selection_test(
@@ -180,6 +232,8 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA|</div>',
+  usesBidiAffinity
+    ? '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4|\u05EA</div>'
+    : '<div contenteditable dir="ltr">\u05E6\u05DC\u05D7\u05EA\n        <div></div>\n        \u05DE\u05E6\u05E0\u05E4\u05EA|</div>',
   '24-29 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/spelling/context_click_select_misspelling.html b/third_party/blink/web_tests/editing/spelling/context_click_select_misspelling.html
index 4598373..98cf984 100644
--- a/third_party/blink/web_tests/editing/spelling/context_click_select_misspelling.html
+++ b/third_party/blink/web_tests/editing/spelling/context_click_select_misspelling.html
@@ -37,6 +37,8 @@
   const textNode = findTextNode(container);
   const range = document.createRange();
   range.setStart(textNode, offset);
+  // Clear mouse button states from last runs.
+  eventSender.setMouseButtonState(-1, []);
 
   const rect = range.getClientRects()[0];
   const x = document.offsetLeft + rect.left + 2;
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_mouse_capture_change_hover.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_mouse_capture_change_hover.html
new file mode 100644
index 0000000..10b19f8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_mouse_capture_change_hover.html
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<style>
+body {
+  user-select: none;
+}
+#green:hover {
+  background-color: red;
+}
+#blue:hover {
+  background-color: red;
+}
+#green {
+  background-color: green;
+}
+#blue {
+  background-color: blue;
+}
+div.box {
+  margin: 5px;
+  padding: 20px;
+  width: 50px;
+  height: 50px;
+}
+</style>
+<body onload="run()">
+  <div id="green" class="box"></div>
+  <div id="blue" class="box"></div>
+</body>
+<script>
+var receivedEventList = [];
+var setcapture = "";
+
+let testEventList = ['pointerup', 'pointerdown', 'pointermove', 'gotpointercapture', 'lostpointercapture', "pointerover", "pointerout", "pointerleave", "pointerenter"];
+testEventList.forEach(function(eventName) {
+  green.addEventListener(eventName, logEvent);
+  blue.addEventListener(eventName, logEvent);
+});
+
+function logEvent(event) {
+  receivedEventList.push(event.target.id + " received " + event.type)
+}
+
+function setCaptureGreen(event) {
+  green.setPointerCapture(event.pointerId);
+}
+
+function setCaptureBlue(event) {
+  blue.setPointerCapture(event.pointerId);
+}
+
+function releaseCapture(event) {
+  if (event.target.hasPointerCapture(event.pointerId)) {
+    event.target.releasePointerCapture(event.pointerId);
+  }
+}
+
+function run() {
+  promise_test (async() => {
+    // Move to (0, 0) to reset hovering.
+    await new test_driver.Actions().pointerMove(0, 0).send();
+    receivedEventList = [];
+
+    // pointerdown at green -> set capture to green -> green receive the following moves.
+    document.addEventListener("pointerdown", setCaptureGreen);
+
+    await new test_driver.Actions()
+                         .pointerMove(25, 25, {origin: green})
+                         .pointerDown()
+                         .pointerMove(30, 30, {origin: green})
+                         .pointerMove(25, 25, {origin: blue})
+                         .send();
+
+    expectedEventList = ["green received pointerover",
+                         "green received pointerenter",
+                         "green received pointermove",
+                         "green received pointerdown",
+                         "green received gotpointercapture",
+                         "green received pointermove",
+                         "green received pointermove"];
+
+    assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+    assert_equals(getComputedStyle(green).backgroundColor, "rgb(255, 0, 0)", "green should be red (hover).");
+    assert_equals(getComputedStyle(blue).backgroundColor, "rgb(0, 0, 255)", "blue should be blue.");
+    document.removeEventListener("pointerdown", setCaptureGreen);
+    // Release mouse button.
+    await new test_driver.Actions().pointerUp().send();
+  }, "Mouse down and capture to green.");
+
+  promise_test (async() => {
+    // Move to (0, 0) to reset hovering.
+    await new test_driver.Actions().pointerMove(0, 0).send();
+    receivedEventList = [];
+
+    // pointerdown at green -> set capture to blue -> blue receive the following moves.
+    document.addEventListener("pointerdown", setCaptureBlue);
+
+    await new test_driver.Actions()
+                         .pointerMove(25, 25, {origin: green})
+                         .pointerDown()
+                         .pointerMove(30, 30, {origin: green})
+                         .pointerMove(30, 30, {origin: green})
+                         .send();
+
+    expectedEventList = ["green received pointerover",
+                         "green received pointerenter",
+                         "green received pointermove",
+                         "green received pointerdown",
+                         "green received pointerout",
+                         "green received pointerleave",
+                         "blue received pointerover",
+                         "blue received pointerenter",
+                         "blue received gotpointercapture",
+                         "blue received pointermove",
+                         "blue received pointermove"];
+
+    assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+    assert_equals(getComputedStyle(green).backgroundColor, "rgb(0, 128, 0)", "green should be green.");
+    assert_equals(getComputedStyle(blue).backgroundColor, "rgb(255, 0, 0)", "blue should be red (hover).");
+    document.removeEventListener("pointerdown", setCaptureBlue);
+    // Release mouse button.
+    await new test_driver.Actions().pointerUp().send();
+  }, "Mouse down at green and capture to blue.");
+
+  promise_test (async() => {
+    // Move to (0, 0) to reset hovering.
+    await new test_driver.Actions().pointerMove(0, 0).send();
+    receivedEventList = [];
+
+    // pointerdown at green -> set capture to green -> green receive first move -> release capture -> blue receive the next move
+    green.addEventListener("pointerdown", setCaptureGreen);
+    green.addEventListener("pointermove", releaseCapture);
+
+    await new test_driver.Actions()
+                         .pointerMove(25, 25, {origin: green})
+                         .pointerDown()
+                         .pointerMove(30, 30, {origin: blue})
+                         .pointerMove(25, 25, {origin: blue})
+                         .send();
+
+    expectedEventList = ["green received pointerover",
+                         "green received pointerenter",
+                         "green received pointermove",
+                         "green received pointerdown",
+                         "green received gotpointercapture",
+                         "green received pointermove",
+                         "green received lostpointercapture",
+                         "green received pointerout",
+                         "green received pointerleave",
+                         "blue received pointerover",
+                         "blue received pointerenter",
+                         "blue received pointermove"]
+
+    assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+    assert_equals(getComputedStyle(green).backgroundColor, "rgb(0, 128, 0)", "green should be green.");
+    assert_equals(getComputedStyle(blue).backgroundColor, "rgb(255, 0, 0)", "blue should be red (hover).");
+    green.removeEventListener("pointerdown", setCaptureBlue);
+    green.removeEventListener("pointermove", releaseCapture);
+    // Release mouse button.
+    await new test_driver.Actions().pointerUp().send();
+  }, "Mouse down and capture to green, move to blue and release capture");
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_mouse_pointercapture_in_frame.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_mouse_pointercapture_in_frame.html
new file mode 100644
index 0000000..83b4c1b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_mouse_pointercapture_in_frame.html
@@ -0,0 +1,143 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<style>
+iframe {
+  width: 300px;
+  height: 300px;
+  top: 100px;
+  left: 100px;
+  border: 0;
+  position: absolute;
+  background: green;
+}
+#outerFrame {
+  width: 500px;
+  height: 500px;
+  background: blue;
+}
+</style>
+<body id="outerFrame body" onload="run()">
+<div id='outerFrame'>
+<iframe id='innerFrameElement' src="resources/pointerevent_mouse_pointercapture-iframe.html"></iframe>
+</div>
+</body>
+<script>
+var receivedEventList = [];
+function handleEvent(event) {
+  receivedEventList.push(event.target.id + ' received ' + event.type);
+
+  if (event.type == 'pointerdown') {
+    if (document.setPointerCaptureOnPointerDown) {
+      event.target.setPointerCapture(event.pointerId);
+    }
+  }
+
+  if (event.type == "pointermove") {
+    if (document.releasePointerCaptureOnFirstMove && event.target.hasPointerCapture(event.pointerId))
+        event.target.releasePointerCapture(event.pointerId);
+  }
+};
+
+document.testEventList = ['pointerup', 'pointerdown', 'pointermove', 'gotpointercapture', 'lostpointercapture'];
+document.testEventList.forEach(function(eventName) {
+  document.getElementById('outerFrame').addEventListener(eventName, handleEvent);
+});
+
+document.setPointerCaptureOnPointerDown = false;
+document.releasePointerCaptureOnFirstMove = false;
+
+function run() {
+    promise_test (async() => {
+        document.setPointerCaptureOnPointerDown = true;
+        receivedEventList = [];
+        expectedEventList = ["innerFrame received pointermove",
+                             "innerFrame received pointerdown",
+                             "innerFrame received gotpointercapture",
+                             "innerFrame received pointermove",
+                             "innerFrame received pointermove",
+                             "innerFrame received pointerup",
+                             "innerFrame received lostpointercapture"];
+        await new test_driver.Actions()
+                             .pointerMove(200, 200)
+                             .pointerDown()
+                             .pointerMove(150, 150)
+                             .pointerMove(50, 50)
+                             .pointerUp()
+                             .send();
+        assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+        document.setPointerCaptureOnPointerDown = false;
+    }, "Test pointer capture event route across the same-origin frame: Mouse down at inner frame and set pointer capture.");
+
+
+    promise_test (async() => {
+        document.setPointerCaptureOnPointerDown = true;
+        receivedEventList = [];
+        expectedEventList = ["outerFrame received pointermove",
+                             "outerFrame received pointerdown",
+                             "outerFrame received gotpointercapture",
+                             "outerFrame received pointermove",
+                             "outerFrame received pointerup",
+                             "outerFrame received lostpointercapture"];
+        await new test_driver.Actions()
+                             .pointerMove(25, 25)
+                             .pointerDown()
+                             .pointerMove(200, 200)
+                             .pointerUp()
+                             .send();
+        assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+        document.setPointerCaptureOnPointerDown = false;
+    }, "Test pointer capture event route across the same-origin frame: Mouse down at outer frame body and set pointer capture.");
+
+
+    promise_test (async() => {
+        document.setPointerCaptureOnPointerDown = true;
+        document.releasePointerCaptureOnFirstMove = true;
+        receivedEventList = [];
+        expectedEventList = ["innerFrame received pointermove",
+                             "innerFrame received pointerdown",
+                             "innerFrame received gotpointercapture",
+                             "innerFrame received pointermove",
+                             "innerFrame received lostpointercapture",
+                             "innerFrameDocument received pointermove",
+                             "innerFrameDocument received pointerup",];
+        await new test_driver.Actions()
+                             .pointerMove(200, 200)
+                             .pointerDown()
+                             .pointerMove(150, 150)
+                             .pointerMove(50, 50)
+                             .pointerUp()
+                             .send();
+        assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+        document.releasePointerCaptureOnFirstMove = false;
+        document.setPointerCaptureOnPointerDown = false;
+    }, "Test pointer capture event route across the same-origin frame: Mouse down with set capture at inner frame, then release on next mouse move.");
+
+
+    promise_test (async() => {
+        document.setPointerCaptureOnPointerDown = true;
+        document.releasePointerCaptureOnFirstMove = true;
+        receivedEventList = [];
+        expectedEventList = ["outerFrame received pointermove",
+                             "outerFrame received pointerdown",
+                             "outerFrame received gotpointercapture",
+                             "outerFrame received pointermove",
+                             "outerFrame received lostpointercapture",
+                             "innerFrame received pointermove",
+                             "innerFrame received pointerup"];
+        await new test_driver.Actions()
+                             .pointerMove(50, 50)
+                             .pointerDown()
+                             .pointerMove(200, 200)
+                             .pointerMove(250, 250)
+                             .pointerUp()
+                             .send();
+        assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+        document.releasePointerCaptureOnFirstMove = false;
+        document.setPointerCaptureOnPointerDown = false;
+    }, "Test pointercapture event route across the same-origin frame: Mouse down with set capture at outer frame, then release on next mouse move.");
+}
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html
new file mode 100644
index 0000000..524d19e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<style>
+    iframe {
+      width: 300px;
+      height: 300px;
+      top: 100px;
+      left: 50px;
+      border: 0;
+      position: absolute;
+      background: green;
+    }
+    #outerFrame {
+      width: 500px;
+      height: 500px;
+      background: blue;
+    }
+</style>
+<body onload="run()">
+    <div id='outerFrame'>
+        <iframe id='innerFrameElement' src="resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html"></iframe>
+    </div>
+</body>
+
+<script type="text/javascript">
+    var test_pointerEvent = async_test("setPointerCapture: outer frame capture pointer active in inner frame");
+
+    document.addEventListener("gotpointercapture", function(){
+        test_pointerEvent.step(function() {
+            assert_unreached("It should not be possible to capture mouse pointer when it's activate in inner frame");
+        });
+    })
+
+    function captureMousePointer(event) {
+        outerFrame.setPointerCapture(event.pointerId);
+    }
+
+    function finishTest() {
+        test_pointerEvent.done();
+    }
+
+    function run() {
+        new test_driver.Actions()
+            .pointerMove(200, 200)
+            .pointerDown()
+            .pointerMove(250, 250)
+            .pointerUp()
+            .send();
+    }
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/resources/pointerevent_mouse_pointercapture-iframe.html b/third_party/blink/web_tests/external/wpt/pointerevents/resources/pointerevent_mouse_pointercapture-iframe.html
new file mode 100644
index 0000000..817c612
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/resources/pointerevent_mouse_pointercapture-iframe.html
@@ -0,0 +1,9 @@
+<html id='innerFrameDocument'>
+  <body id='innerFrame' style='height:500px; width: 500px; padding: 0; margin: 0;'>
+    <script>
+      top.document.testEventList.forEach(function(eventName) {
+        document.addEventListener(eventName, top.handleEvent);
+      });
+    </script>
+  </body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html b/third_party/blink/web_tests/external/wpt/pointerevents/resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html
new file mode 100644
index 0000000..d4b4af1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html
@@ -0,0 +1,10 @@
+<body id='innerFrame' style='height:500px; width: 500px; padding: 0; margin: 0;'>
+  <script>
+      document.addEventListener('pointerdown', function(event) {
+          top.captureMousePointer(event);
+      });
+      document.addEventListener('pointerup', function(event) {
+          top.finishTest();
+      });
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/mock-barcodedetection.js b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-barcodedetection.js
new file mode 100644
index 0000000..2558bbda
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-barcodedetection.js
@@ -0,0 +1,124 @@
+"use strict";
+
+var BarcodeDetectionTest = (() => {
+  // Class that mocks BarcodeDetectionProvider interface defined in
+  // https://cs.chromium.org/chromium/src/services/shape_detection/public/mojom/barcodedetection_provider.mojom
+  class MockBarcodeDetectionProvider {
+    constructor() {
+      this.bindingSet_ = new mojo.BindingSet(
+          shapeDetection.mojom.BarcodeDetectionProvider);
+
+      this.interceptor_ = new MojoInterfaceInterceptor(
+          shapeDetection.mojom.BarcodeDetectionProvider.name);
+      this.interceptor_.oninterfacerequest =
+         e => this.bindingSet_.addBinding(this, e.handle);
+      this.interceptor_.start();
+    }
+
+    createBarcodeDetection(request, options) {
+      this.mockService_ = new MockBarcodeDetection(request, options);
+    }
+
+    enumerateSupportedFormats() {
+      return Promise.resolve({
+        supportedFormats: [
+          shapeDetection.mojom.BarcodeFormat.AZTEC,
+          shapeDetection.mojom.BarcodeFormat.DATA_MATRIX,
+          shapeDetection.mojom.BarcodeFormat.QR_CODE,
+        ]
+      });
+    }
+
+    getFrameData() {
+      return this.mockService_.bufferData_;
+    }
+
+    getFormats() {
+     return this.mockService_.options_.formats;
+    }
+
+    reset() {
+      this.mockService_ = null;
+      this.bindingSet_.closeAllBindings();
+      this.interceptor_.stop();
+    }
+  }
+
+  // Class that mocks BarcodeDetection interface defined in
+  // https://cs.chromium.org/chromium/src/services/shape_detection/public/mojom/barcodedetection.mojom
+  class MockBarcodeDetection {
+    constructor(request, options) {
+      this.options_ = options;
+      this.binding_ =
+          new mojo.Binding(shapeDetection.mojom.BarcodeDetection,
+                           this, request);
+    }
+
+    detect(bitmapData) {
+      this.bufferData_ =
+          new Uint32Array(getArrayBufferFromBigBuffer(bitmapData.pixelData));
+      return Promise.resolve({
+        results: [
+          {
+            rawValue : "cats",
+            boundingBox: { x: 1.0, y: 1.0, width: 100.0, height: 100.0 },
+            format: shapeDetection.mojom.BarcodeFormat.QR_CODE,
+            cornerPoints: [
+              { x: 1.0, y: 1.0 },
+              { x: 101.0, y: 1.0 },
+              { x: 101.0, y: 101.0 },
+              { x: 1.0, y: 101.0 }
+            ],
+          },
+          {
+            rawValue : "dogs",
+            boundingBox: { x: 2.0, y: 2.0, width: 50.0, height: 50.0 },
+            format: shapeDetection.mojom.BarcodeFormat.CODE_128,
+            cornerPoints: [
+              { x: 2.0, y: 2.0 },
+              { x: 52.0, y: 2.0 },
+              { x: 52.0, y: 52.0 },
+              { x: 2.0, y: 52.0 }
+            ],
+          },
+        ],
+      });
+    }
+  }
+
+  let testInternal = {
+    initialized: false,
+    MockBarcodeDetectionProvider: null
+  }
+
+  class BarcodeDetectionTestChromium {
+    constructor() {
+      Object.freeze(this); // Make it immutable.
+    }
+
+    initialize() {
+      if (testInternal.initialized)
+        throw new Error('Call reset() before initialize().');
+
+      testInternal.MockBarcodeDetectionProvider = new MockBarcodeDetectionProvider;
+      testInternal.initialized = true;
+    }
+
+    // Resets state of barcode detection mocks between test runs.
+    async reset() {
+      if (!testInternal.initialized)
+        throw new Error('Call initialize() before reset().');
+      testInternal.MockBarcodeDetectionProvider.reset();
+      testInternal.MockBarcodeDetectionProvider = null;
+      testInternal.initialized = false;
+
+      await new Promise(resolve => setTimeout(resolve, 0));
+    }
+
+    MockBarcodeDetectionProvider() {
+      return testInternal.MockBarcodeDetectionProvider;
+    }
+  }
+
+  return BarcodeDetectionTestChromium;
+})();
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/mock-barcodedetection.js.headers b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-barcodedetection.js.headers
new file mode 100644
index 0000000..6c61a34
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-barcodedetection.js.headers
@@ -0,0 +1 @@
+Content-Type: text/javascript; charset=utf-8
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/mock-facedetection.js b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-facedetection.js
new file mode 100644
index 0000000..1275e4dd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-facedetection.js
@@ -0,0 +1,131 @@
+"use strict";
+
+var FaceDetectionTest = (() => {
+  // Class that mocks FaceDetectionProvider interface defined in
+  // https://cs.chromium.org/chromium/src/services/shape_detection/public/mojom/facedetection_provider.mojom
+  class MockFaceDetectionProvider {
+    constructor() {
+      this.bindingSet_ = new mojo.BindingSet(
+        shapeDetection.mojom.FaceDetectionProvider);
+
+      this.interceptor_ = new MojoInterfaceInterceptor(
+          shapeDetection.mojom.FaceDetectionProvider.name);
+      this.interceptor_.oninterfacerequest =
+         e => this.bindingSet_.addBinding(this, e.handle);
+      this.interceptor_.start();
+    }
+
+    createFaceDetection(request, options) {
+      this.mockService_ = new MockFaceDetection(request, options);
+    }
+
+    getFrameData() {
+      return this.mockService_.bufferData_;
+    }
+
+    getMaxDetectedFaces() {
+     return this.mockService_.maxDetectedFaces_;
+    }
+
+    getFastMode () {
+      return this.mockService_.fastMode_;
+    }
+
+    reset() {
+      this.mockService_ = null;
+      this.bindingSet_.closeAllBindings();
+      this.interceptor_.stop();
+    }
+  }
+
+  // Class that mocks FaceDetection interface defined in
+  // https://cs.chromium.org/chromium/src/services/shape_detection/public/mojom/facedetection.mojom
+  class MockFaceDetection {
+    constructor(request, options) {
+      this.maxDetectedFaces_ = options.maxDetectedFaces;
+      this.fastMode_ = options.fastMode;
+      this.binding_ =
+          new mojo.Binding(shapeDetection.mojom.FaceDetection,
+                           this, request);
+    }
+
+    detect(bitmapData) {
+      this.bufferData_ =
+          new Uint32Array(getArrayBufferFromBigBuffer(bitmapData.pixelData));
+      return Promise.resolve({
+        results: [
+          {
+            boundingBox: {x: 1.0, y: 1.0, width: 100.0, height: 100.0},
+            landmarks: [{
+              type: shapeDetection.mojom.LandmarkType.EYE,
+              locations: [{x: 4.0, y: 5.0}]
+            },
+            {
+              type: shapeDetection.mojom.LandmarkType.EYE,
+              locations: [
+                {x: 4.0, y: 5.0}, {x: 5.0, y: 4.0}, {x: 6.0, y: 3.0},
+                {x: 7.0, y: 4.0}, {x: 8.0, y: 5.0}, {x: 7.0, y: 6.0},
+                {x: 6.0, y: 7.0}, {x: 5.0, y: 6.0}
+              ]
+            }]
+          },
+          {
+            boundingBox: {x: 2.0, y: 2.0, width: 200.0, height: 200.0},
+            landmarks: [{
+              type: shapeDetection.mojom.LandmarkType.NOSE,
+              locations: [{x: 100.0, y: 50.0}]
+            },
+            {
+              type: shapeDetection.mojom.LandmarkType.NOSE,
+              locations: [
+                {x: 80.0, y: 50.0}, {x: 70.0, y: 60.0}, {x: 60.0, y: 70.0},
+                {x: 70.0, y: 60.0}, {x: 80.0, y: 70.0}, {x: 90.0, y: 80.0},
+                {x: 100.0, y: 70.0}, {x: 90.0, y: 60.0}, {x: 80.0, y: 50.0}
+              ]
+            }]
+          },
+          {
+            boundingBox: {x: 3.0, y: 3.0, width: 300.0, height: 300.0},
+            landmarks: []
+          },
+        ]
+      });
+    }
+  }
+
+  let testInternal = {
+    initialized: false,
+    MockFaceDetectionProvider: null
+  }
+
+  class FaceDetectionTestChromium {
+    constructor() {
+      Object.freeze(this); // Make it immutable.
+    }
+
+    initialize() {
+      if (testInternal.initialized)
+        throw new Error('Call reset() before initialize().');
+
+      testInternal.MockFaceDetectionProvider = new MockFaceDetectionProvider;
+      testInternal.initialized = true;
+    }
+
+    // Resets state of face detection mocks between test runs.
+    async reset() {
+      if (!testInternal.initialized)
+        throw new Error('Call initialize() before reset().');
+      testInternal.MockFaceDetectionProvider.reset();
+      testInternal.MockFaceDetectionProvider = null;
+      testInternal.initialized = false;
+
+      await new Promise(resolve => setTimeout(resolve, 0));
+    }
+
+    MockFaceDetectionProvider() {
+      return testInternal.MockFaceDetectionProvider;
+    }
+  }
+
+  return FaceDetectionTestChromium;
+})();
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/mock-facedetection.js.headers b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-facedetection.js.headers
new file mode 100644
index 0000000..6c61a34
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-facedetection.js.headers
@@ -0,0 +1 @@
+Content-Type: text/javascript; charset=utf-8
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/extendable-event-async-waituntil.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/extendable-event-async-waituntil.https.html
index cb4ed30a..04e9826 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/extendable-event-async-waituntil.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/extendable-event-async-waituntil.https.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="resources/testharness-helpers.js"></script>
 <script src="/resources/testharnessreport.js"></script>
@@ -56,21 +57,25 @@
 }
 
 promise_test(msg_event_test.bind(this, 'no-current-extension-different-task'),
-  'Test calling waitUntil in a different task without an existing extension throws');
+  'Test calling waitUntil in a task at the end of the event handler without an existing extension throws');
 
 promise_test(msg_event_test.bind(this, 'no-current-extension-different-microtask'),
-  'Test calling waitUntil in a different microtask without an existing extension throws');
+  'Test calling waitUntil in a microtask at the end of the event handler without an existing extension suceeds');
 
 promise_test(msg_event_test.bind(this, 'current-extension-different-task'),
-  'Test calling waitUntil in a different task with an existing extension succeeds');
+  'Test calling waitUntil in a different task an existing extension succeeds');
 
-promise_test(msg_event_test.bind(this, 'current-extension-expired-same-microtask-turn'),
-  'Test calling waitUntil with an existing extension promise handler succeeds');
+promise_test(msg_event_test.bind(this, 'during-event-dispatch-current-extension-expired-same-microtask-turn'),
+  'Test calling waitUntil at the end of an existing extension promise handler succeeds (event is still being dispatched)');
 
-// The promise handler will queue a new microtask after the check for new
-// extensions was performed.
-promise_test(msg_event_test.bind(this, 'current-extension-expired-same-microtask-turn-extra'),
-  'Test calling waitUntil at the end of the microtask turn throws');
+promise_test(msg_event_test.bind(this, 'during-event-dispatch-current-extension-expired-same-microtask-turn-extra'),
+  'Test calling waitUntil in a microtask at the end of an existing extension promise handler succeeds (event is still being dispatched)');
+
+promise_test(msg_event_test.bind(this, 'after-event-dispatch-current-extension-expired-same-microtask-turn'),
+  'Test calling waitUntil in an existing extension promise handler succeeds (event is not being dispatched)');
+
+promise_test(msg_event_test.bind(this, 'after-event-dispatch-current-extension-expired-same-microtask-turn-extra'),
+  'Test calling waitUntil in a microtask at the end of an existing extension promise handler throws (event is not being dispatched)');
 
 promise_test(msg_event_test.bind(this, 'current-extension-expired-different-task'),
   'Test calling waitUntil after the current extension expired in a different task fails');
@@ -80,24 +85,36 @@
 
 promise_test(function(t) {
     var testBody = function(worker) {
-      return with_iframe('./resources/pending-respondwith-async-waituntil/dummy.html');
+      return with_iframe('./resources/pending-respondwith-async-waituntil');
     }
     return runTest(t, 'pending-respondwith-async-waituntil', testBody);
   }, 'Test calling waitUntil asynchronously with pending respondWith promise.');
 
 promise_test(function(t) {
     var testBody = function(worker) {
-      return with_iframe('./resources/respondwith-microtask-sync-waituntil/dummy.html');
+      return with_iframe('./resources/during-event-dispatch-respondwith-microtask-sync-waituntil');
     }
-    return runTest(t, 'respondwith-microtask-sync-waituntil', testBody);
-  }, 'Test calling waitUntil synchronously inside microtask of respondWith promise.');
+    return runTest(t, 'during-event-dispatch-respondwith-microtask-sync-waituntil', testBody);
+  }, 'Test calling waitUntil synchronously inside microtask of respondWith promise (event is being dispatched).');
 
 promise_test(function(t) {
     var testBody = function(worker) {
-      return with_iframe('./resources/respondwith-microtask-async-waituntil/dummy.html');
+      return with_iframe('./resources/during-event-dispatch-respondwith-microtask-async-waituntil');
     }
-    return runTest(t, 'respondwith-microtask-async-waituntil', testBody);
-  }, 'Test calling waitUntil asynchronously inside microtask of respondWith promise.');
+    return runTest(t, 'during-event-dispatch-respondwith-microtask-async-waituntil', testBody);
+  }, 'Test calling waitUntil asynchronously inside microtask of respondWith promise (event is being dispatched).');
 
+promise_test(function(t) {
+    var testBody = function(worker) {
+      return with_iframe('./resources/after-event-dispatch-respondwith-microtask-sync-waituntil');
+    }
+    return runTest(t, 'after-event-dispatch-respondwith-microtask-sync-waituntil', testBody);
+  }, 'Test calling waitUntil synchronously inside microtask of respondWith promise (event is not being dispatched).');
 
+promise_test(function(t) {
+    var testBody = function(worker) {
+      return with_iframe('./resources/after-event-dispatch-respondwith-microtask-async-waituntil');
+    }
+    return runTest(t, 'after-event-dispatch-respondwith-microtask-async-waituntil', testBody);
+  }, 'Test calling waitUntil asynchronously inside microtask of respondWith promise (event is not being dispatched).');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-async-respond-with.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-async-respond-with.https.html
index 87fa046..7842a82 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-async-respond-with.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-async-respond-with.https.html
@@ -1,36 +1,61 @@
 <!DOCTYPE html>
+<html>
+<title>respondWith cannot be called asynchronously</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/test-helpers.sub.js"></script>
 <script>
-promise_test(function(t) {
-    var script = 'resources/fetch-event-async-respond-with-worker.js';
-    var scope = 'resources/simple.html';
+// This file has tests that call respondWith() asynchronously.
 
-    return service_worker_unregister_and_register(t, script, scope)
-      .then(function(registration) {
-          t.add_cleanup(function() {
-              return service_worker_unregister(t, scope);
-            });
+let frame;
+let worker;
+const script = 'resources/fetch-event-async-respond-with-worker.js';
+const scope = 'resources/simple.html';
 
-          return wait_for_state(t, registration.installing, 'activated');
-        })
-      .then(function() {
-          return with_iframe(scope);
-        })
-      .then(function(frame) {
-          add_completion_callback(function() { frame.remove(); });
-          var channel = new MessageChannel();
-          var saw_message = new Promise(function(resolve) {
-              channel.port1.onmessage = function(e) { resolve(e.data); }
-            });
-          var worker = frame.contentWindow.navigator.serviceWorker.controller;
+// Global setup: this must be the first promise_test.
+promise_test(async (t) => {
+  const registration =
+      await service_worker_unregister_and_register(t, script, scope);
+  worker = registration.installing;
+  await wait_for_state(t, worker, 'activated');
+  frame = await with_iframe(scope);
+}, 'global setup');
 
-          worker.postMessage({port: channel.port2}, [channel.port2]);
-          return saw_message;
-        })
-      .then(function(message) {
-          assert_equals(message, 'PASS');
-        })
-  }, 'Calling respondWith asynchronously throws an exception');
+// Does one test case. It fetches |url|. The service worker gets a fetch event
+// for |url| and attempts to call respondWith() asynchronously. It reports back
+// to the test whether an exception was thrown.
+async function do_test(url) {
+  // Send a message to tell the worker a new test case is starting.
+  const sawMessage = new Promise(resolve => {
+    navigator.serviceWorker.onmessage = (event) => {
+      resolve(event.data);
+    };
+    worker.postMessage('');
+  });
+
+  // Start a fetch.
+  frame.contentWindow.fetch(url);
+
+  // Receive the test result from the service worker.
+  return await sawMessage;
+};
+
+promise_test(async (t) => {
+  const result = await do_test('respondWith-in-task');
+  assert_true(result.didThrow, 'should throw');
+  assert_equals(result.error, 'InvalidStateError');
+}, 'respondWith in a task throws InvalidStateError');
+
+promise_test(async (t) => {
+  const result = await do_test('respondWith-in-microtask');
+  assert_equals(result.didThrow, false, 'should not throw');
+}, 'respondWith in a microtask does not throw');
+
+// Global cleanup: the final promise_test.
+promise_test(async (t) => {
+  if (frame)
+    frame.remove();
+  await service_worker_unregister(t, scope);
+}, 'global cleanup');
 </script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/extendable-event-async-waituntil.js b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/extendable-event-async-waituntil.js
index abf54934..8a975b0 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/extendable-event-async-waituntil.js
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/extendable-event-async-waituntil.js
@@ -1,4 +1,12 @@
-// controlled by 'init'/'done' messages.
+// This worker calls waitUntil() and respondWith() asynchronously and
+// reports back to the test whether they threw.
+//
+// These test cases are confusing. Bear in mind that the event is active
+// (calling waitUntil() is allowed) if:
+// * The pending promise count is not 0, or
+// * The event dispatch flag is set.
+
+// Controlled by 'init'/'done' messages.
 var resolveLockPromise;
 var port;
 
@@ -14,34 +22,72 @@
       case 'done':
         resolveLockPromise();
         break;
+
+      // Throws because waitUntil() is called in a task after event dispatch
+      // finishes.
       case 'no-current-extension-different-task':
         async_task_waituntil(event).then(reportResultExpecting('InvalidStateError'));
         break;
+
+      // OK because waitUntil() is called in a microtask that runs after the
+      // event handler runs, while the event dispatch flag is still set.
       case 'no-current-extension-different-microtask':
-        async_microtask_waituntil(event).then(reportResultExpecting('InvalidStateError'));
+        async_microtask_waituntil(event).then(reportResultExpecting('OK'));
         break;
+
+      // OK because the second waitUntil() is called while the first waitUntil()
+      // promise is still pending.
       case 'current-extension-different-task':
         event.waitUntil(new Promise((res) => { resolveTestPromise = res; }));
         async_task_waituntil(event).then(reportResultExpecting('OK')).then(resolveTestPromise);
         break;
-      case 'current-extension-expired-same-microtask-turn':
+
+      // OK because all promises involved resolve "immediately", so the second
+      // waitUntil() is called during the microtask checkpoint at the end of
+      // event dispatching, when the event dispatch flag is still set.
+      case 'during-event-dispatch-current-extension-expired-same-microtask-turn':
         waitPromise = Promise.resolve();
         event.waitUntil(waitPromise);
         waitPromise.then(() => { return sync_waituntil(event); })
           .then(reportResultExpecting('OK'))
         break;
-      case 'current-extension-expired-same-microtask-turn-extra':
-        // The promise handler queues a new microtask *after* the check for new
-        // extensions was performed.
+
+      // OK for the same reason as above.
+      case 'during-event-dispatch-current-extension-expired-same-microtask-turn-extra':
         waitPromise = Promise.resolve();
         event.waitUntil(waitPromise);
         waitPromise.then(() => { return async_microtask_waituntil(event); })
+          .then(reportResultExpecting('OK'))
+        break;
+
+
+      // OK because the pending promise count is decremented in a microtask
+      // queued upon fulfillment of the first waitUntil() promise, so the second
+      // waitUntil() is called while the pending promise count is still
+      // positive.
+      case 'after-event-dispatch-current-extension-expired-same-microtask-turn':
+        waitPromise = makeNewTaskPromise();
+        event.waitUntil(waitPromise);
+        waitPromise.then(() => { return sync_waituntil(event); })
+          .then(reportResultExpecting('OK'))
+        break;
+
+      // Throws because the second waitUntil() is called after the pending
+      // promise count was decremented to 0.
+      case 'after-event-dispatch-current-extension-expired-same-microtask-turn-extra':
+        waitPromise = makeNewTaskPromise();
+        event.waitUntil(waitPromise);
+        waitPromise.then(() => { return async_microtask_waituntil(event); })
           .then(reportResultExpecting('InvalidStateError'))
         break;
+
+      // Throws because the second waitUntil() is called in a new task, after
+      // first waitUntil() promise settled and the event dispatch flag is unset.
       case 'current-extension-expired-different-task':
         event.waitUntil(Promise.resolve());
         async_task_waituntil(event).then(reportResultExpecting('InvalidStateError'));
         break;
+
       case 'script-extendable-event':
         self.dispatchEvent(new ExtendableEvent('nontrustedevent'));
         break;
@@ -51,25 +97,62 @@
   });
 
 self.addEventListener('fetch', function(event) {
-    if (event.request.url.indexOf('pending-respondwith-async-waituntil') != -1) {
+  const path = new URL(event.request.url).pathname;
+  const step = path.substring(path.lastIndexOf('/') + 1);
+  let response;
+  switch (step) {
+    // OK because waitUntil() is called while the respondWith() promise is still
+    // unsettled, so the pending promise count is positive.
+    case 'pending-respondwith-async-waituntil':
       var resolveFetch;
-      let response = new Promise((res) => { resolveFetch = res; });
+      response = new Promise((res) => { resolveFetch = res; });
       event.respondWith(response);
       async_task_waituntil(event)
         .then(reportResultExpecting('OK'))
         .then(() => { resolveFetch(new Response('OK')); });
-    } else if (event.request.url.indexOf('respondwith-microtask-sync-waituntil') != -1) {
+      break;
+
+    // OK because all promises involved resolve "immediately", so waitUntil() is
+    // called during the microtask checkpoint at the end of event dispatching,
+    // when the event dispatch flag is still set.
+    case 'during-event-dispatch-respondwith-microtask-sync-waituntil':
       response = Promise.resolve(new Response('RESP'));
       event.respondWith(response);
       response.then(() => { return sync_waituntil(event); })
-        .then(reportResultExpecting('OK'))
-    } else if (event.request.url.indexOf('respondwith-microtask-async-waituntil') != -1) {
+        .then(reportResultExpecting('OK'));
+      break;
+
+    // OK because all promises involved resolve "immediately", so waitUntil() is
+    // called during the microtask checkpoint at the end of event dispatching,
+    // when the event dispatch flag is still set.
+    case 'during-event-dispatch-respondwith-microtask-async-waituntil':
       response = Promise.resolve(new Response('RESP'));
       event.respondWith(response);
       response.then(() => { return async_microtask_waituntil(event); })
+        .then(reportResultExpecting('OK'));
+      break;
+
+    // OK because the pending promise count is decremented in a microtask queued
+    // upon fulfillment of the respondWith() promise, so waitUntil() is called
+    // while the pending promise count is still positive.
+    case 'after-event-dispatch-respondwith-microtask-sync-waituntil':
+      response = makeNewTaskPromise().then(() => {return new Response('RESP');});
+      event.respondWith(response);
+      response.then(() => { return sync_waituntil(event); })
+        .then(reportResultExpecting('OK'));
+      break;
+
+
+    // Throws because waitUntil() is called after the pending promise count was
+    // decremented to 0.
+    case 'after-event-dispatch-respondwith-microtask-async-waituntil':
+      response = makeNewTaskPromise().then(() => {return new Response('RESP');});
+      event.respondWith(response);
+      response.then(() => { return async_microtask_waituntil(event); })
         .then(reportResultExpecting('InvalidStateError'))
-    }
-  });
+      break;
+  }
+});
 
 self.addEventListener('nontrustedevent', function(event) {
     sync_waituntil(event).then(reportResultExpecting('InvalidStateError'));
@@ -118,3 +201,10 @@
     }, 0);
   });
 }
+
+// Returns a promise that settles in a separate task.
+function makeNewTaskPromise() {
+  return new Promise(resolve => {
+    setTimeout(resolve, 0);
+  });
+}
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js
index 7f66d20..3409d0a 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js
@@ -1,19 +1,56 @@
-var result;
+// This worker attempts to call respondWith() asynchronously after the
+// fetch event handler finished. It reports back to the test whether
+// an exception was thrown.
 
-self.addEventListener('message', function(event) {
-    event.data.port.postMessage(result);
+// These get reset at the start of a test case.
+let reportResult;
+let resultPromise;
+
+// The test page sends a message to tell us that a new test case is starting.
+// We expect a fetch event after this.
+self.addEventListener('message', (event) => {
+  resultPromise = new Promise((resolve) => {
+    reportResult = resolve;
   });
 
+  // Keep the worker alive until the test case finishes, and report
+  // back the result to the test page.
+  event.waitUntil(resultPromise.then(result => {
+    event.source.postMessage(result);
+  }));
+});
+
+// Calls respondWith() and reports back whether an exception occurred.
+function tryRespondWith(event) {
+  try {
+    event.respondWith(new Response());
+    reportResult({didThrow: false});
+  } catch (error) {
+    reportResult({didThrow: true, error: error.name});
+  }
+}
+
+function respondWithInTask(event) {
+  setTimeout(() => {
+    tryRespondWith(event);
+  }, 0);
+}
+
+function respondWithInMicrotask(event) {
+  Promise.resolve().then(() => {
+    tryRespondWith(event);
+  });
+}
+
 self.addEventListener('fetch', function(event) {
-    setTimeout(function() {
-        try {
-          event.respondWith(new Response());
-          result = 'FAIL: did not throw';
-        } catch (error) {
-          if (error.name == 'InvalidStateError')
-            result = 'PASS';
-          else
-            result = 'FAIL: Unexpected exception: ' + error;
-        }
-      }, 0);
-  });
+  const path = new URL(event.request.url).pathname;
+  const test = path.substring(path.lastIndexOf('/') + 1);
+
+  // If this is a test case, try respondWith() and report back to the test page
+  // the result.
+  if (test == 'respondWith-in-task') {
+    respondWithInTask(event);
+  } else if (test == 'respondWith-in-microtask') {
+    respondWithInMicrotask(event);
+  }
+});
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/README.md b/third_party/blink/web_tests/external/wpt/shape-detection/README.md
new file mode 100644
index 0000000..556568b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/README.md
@@ -0,0 +1,44 @@
+The `shapedetection-helpers.js` tests require implementations of
+the `FaceDetectionTest` and `BarcodeDetectionTest` interfaces, which
+should emulate platform shape detection backends.
+
+The `FaceDetectionTest` interface is defined as:
+
+```
+  class FaceDetectionTest {
+    async initialize();  // Sets up the testing environment.
+    async reset(); // Frees the resources.
+    MockFaceDetectionProvider(); //Returns `MockFaceDetectionProvider` interface.
+  };
+
+  class MockFaceDetectionProvider {
+    getFrameData(); //Gets frame data of detection result.
+    getMaxDetectedFaces(); //Gets value of `maxDetectedFaces` in `FaceDetector` constructor
+    getFastMode(); //Gets value of `fastMode` in `FaceDetector` constructor
+  };
+```
+
+The Chromium implementation of the `FaceDetectionTest` interface is located in
+[mock-facedetection.js](../resources/chromium/mock-facedetection.js).
+
+The `BarcodeDetectionTest` interface is defined as:
+
+```
+  class BarcodeDetectionTest {
+    async initialize();  // Sets up the testing environment.
+    async reset(); // Frees the resources.
+    MockBarcodeDetectionProvider(); //Returns `MockBarcodeDetectionProvider` interface.
+  };
+
+  class MockBarcodeDetectionProvider {
+    async enumerateSupportedFormats(); //Enumerates supported formats
+    getFrameData(); //Gets frame data of detection result.
+    getFormats(); //Gets value of `formats` in `BarcodeDetector` constructor
+  };
+```
+
+The Chromium implementation of the `BarcodeDetectionTest` interface is located in
+[mock-barcodedetection.js](../resources/chromium/mock-barcodedetection.js).
+
+Other browser vendors should provide their own implementations of
+the `FaceDetectionTest` and `BarcodeDetectionTest` interfaces.
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detected-boundingBox-read-only.html b/third_party/blink/web_tests/external/wpt/shape-detection/detected-boundingBox-read-only.html
new file mode 100644
index 0000000..86b8889
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/detected-boundingBox-read-only.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that detected{Face, Barcode}'s boundingBox
+// should be DOMRectReadOnly.
+const imageDataTests =
+    [
+      {
+        createDetector: () => { return new FaceDetector(); },
+        mockTestName: "FaceDetectionTest",
+        name: "Face - detectedFace.boundingBox should be DOMRectReadOnly"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        mockTestName: "BarcodeDetectionTest",
+        name: "Barcode - detectedBarcode.boundingBox should be DOMRectReadOnly"
+      }
+    ];
+
+for (let imageDataTest of imageDataTests) {
+  detection_test(imageDataTest.mockTestName, async t => {
+    const img = new Image();
+    const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+    img.src = "/images/green-16x16.png";
+    await imgWatcher.wait_for("load");
+
+    const canvas = document.createElement("canvas");
+    canvas.getContext("2d").drawImage(img, 0, 0);
+
+    const detector = imageDataTest.createDetector();
+    const detectionResult = await detector.detect(canvas.getContext("2d")
+        .getImageData(0, 0, canvas.width, canvas.height));
+    CheckDetectedReadOnlyBoundingBoxes(detectionResult);
+  }, imageDataTest.name);
+}
+
+function CheckDetectedReadOnlyBoundingBoxes(detectedObjects) {
+  const properties =
+      ['x', 'y', 'width', 'height', 'top', 'right', 'bottom', 'left'];
+
+  detectedObjects.forEach(detectedObject => {
+    properties.forEach(property => {
+      assert_readonly(detectedObject.boundingBox, property);
+    });
+  });
+}
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLCanvasElement.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLCanvasElement.html
new file mode 100644
index 0000000..4e9615a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLCanvasElement.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that a Detector's detect() works on an HTMLCanvasElement
+// and on an OffscreenCanvas.
+const canvasElementTests =
+    [
+      {
+        createDetector: () => { return new FaceDetector(); },
+        createCanvas: () => { return document.createElement("canvas"); },
+        mockTestName: "FaceDetectionTest",
+        detectionResultTest: FaceDetectorDetectionResultTest,
+        name: "Face - detect(HTMLCanvasElement)"
+      },
+      {
+        createDetector: () => { return new FaceDetector(); },
+        createCanvas: () => { return new OffscreenCanvas(300, 150); },
+        mockTestName: "FaceDetectionTest",
+        detectionResultTest: FaceDetectorDetectionResultTest,
+        name: "Face - detect(OffscreenCanvas)"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        createCanvas: () => { return document.createElement("canvas"); },
+        mockTestName: "BarcodeDetectionTest",
+        detectionResultTest: BarcodeDetectorDetectionResultTest,
+        name: "Barcode - detect(HTMLCanvasElement)"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        createCanvas: () => { return new OffscreenCanvas(300, 150); },
+        mockTestName: "BarcodeDetectionTest",
+        detectionResultTest: BarcodeDetectorDetectionResultTest,
+        name: "Barcode - detect(OffscreenCanvas)"
+      }
+    ];
+
+for (let canvasElementTest of canvasElementTests) {
+  detection_test(canvasElementTest.mockTestName, async (t, detectionTest) => {
+    const img = new Image();
+    const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+    img.src = "/images/green-16x16.png";
+    await imgWatcher.wait_for("load");
+    const canvas = canvasElementTest.createCanvas();
+    canvas.getContext("2d").drawImage(img, 0, 0);
+
+    const detector = canvasElementTest.createDetector();
+    const detectionResult = await detector.detect(canvas);
+    canvasElementTest.detectionResultTest(detectionResult, detectionTest);
+  }, canvasElementTest.name);
+}
+
+function FaceDetectorDetectionResultTest(detectionResult, mockTest) {
+  const imageReceivedByMock =
+      mockTest.MockFaceDetectionProvider().getFrameData();
+  assert_equals(imageReceivedByMock.byteLength, 180000, "Image length");
+  const GREEN_PIXEL = 0xFF00FF00;
+  assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
+  assert_equals(detectionResult.length, 3, "Number of faces");
+}
+
+function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) {
+  assert_equals(detectionResult.length, 2, "Number of barcodes");
+  assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
+  assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
+  assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
+  assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
+}
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLImageElement.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLImageElement.html
new file mode 100644
index 0000000..979efabd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLImageElement.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<body>
+<img id="img" src="/images/green-16x16.png"/>
+</body>
+<script>
+
+// These tests verify that a Detector's detect() works on an HTMLImageElement.
+const imageElementTests =
+    [
+      {
+        createDetector: () => { return new FaceDetector(); },
+        mockTestName: "FaceDetectionTest",
+        detectionResultTest: FaceDetectorDetectionResultTest,
+        name: "Face - detect(HTMLImageElement)"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        mockTestName: "BarcodeDetectionTest",
+        detectionResultTest: BarcodeDetectorDetectionResultTest,
+        name: "Barcode - detect(HTMLImageElement)",
+      }
+    ];
+
+for (let imageElementTest of imageElementTests) {
+  detection_test(imageElementTest.mockTestName, async (t, detectionTest) => {
+    const img = document.getElementById("img");
+
+    const detector = imageElementTest.createDetector();
+    const detectionResult = await detector.detect(img);
+    imageElementTest.detectionResultTest(detectionResult, detectionTest);
+  }, imageElementTest.name);
+}
+
+function FaceDetectorDetectionResultTest(detectionResult, mockTest) {
+  const imageReceivedByMock =
+      mockTest.MockFaceDetectionProvider().getFrameData();
+  assert_equals(imageReceivedByMock.byteLength, 1024, "Image length");
+  const GREEN_PIXEL = 0xFF00FF00;
+  assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
+  assert_equals(detectionResult.length, 3, "Number of faces");
+  assert_equals(detectionResult[0].landmarks.length, 2, "Number of landmarks");
+  assert_object_equals(detectionResult[0].landmarks[0],
+                      {type : 'eye', locations : [{x : 4.0, y : 5.0}]},
+                      "landmark #1");
+  assert_equals(detectionResult[0].landmarks[1].locations.length, 8,
+                "Number of locations along eye");
+  assert_object_equals(detectionResult[1].landmarks[0],
+                      {type : 'nose', locations : [{x : 100.0, y : 50.0}]},
+                      "landmark #2");
+  assert_equals(detectionResult[1].landmarks[1].locations.length, 9,
+                "Number of locations along nose");
+}
+
+function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) {
+  assert_equals(detectionResult.length, 2, "Number of barcodes");
+  assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
+  assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
+  assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
+  assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
+}
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLVideoElement.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLVideoElement.html
new file mode 100644
index 0000000..7b3736d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLVideoElement.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that a Detector's detect() works on an HTMLVideoElement.
+const videoElementTests =
+    [
+      {
+        createDetector: () => { return new FaceDetector(); },
+        mockTestName: "FaceDetectionTest",
+        detectionResultTest: FaceDetectorDetectionResultTest,
+        name: "Face - detect(HTMLVideoElement)"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        mockTestName: "BarcodeDetectionTest",
+        detectionResultTest: BarcodeDetectorDetectionResultTest,
+        name: "Barcode - detect(HTMLVideoElement)",
+      }
+    ];
+
+for (let videoElementTest of videoElementTests) {
+  detection_test(videoElementTest.mockTestName, async (t, detectionTest) => {
+    const video = document.createElement("video");
+    video.src = "/media/white.webm";
+    video.loop = true;
+    video.autoplay = true;
+    const videoWatcher = new EventWatcher(t, video, ["play", "error"]);
+    video.load();
+    await videoWatcher.wait_for("play");
+
+    const detector = videoElementTest.createDetector();
+    const detectionResult = await detector.detect(video);
+    videoElementTest.detectionResultTest(detectionResult, detectionTest);
+  }, videoElementTest.name);
+}
+
+function FaceDetectorDetectionResultTest(detectionResult, mockTest) {
+  const imageReceivedByMock =
+      mockTest.MockFaceDetectionProvider().getFrameData();
+  assert_equals(imageReceivedByMock.byteLength, 307200, "Image length");
+  assert_equals(detectionResult.length, 3, "Number of faces");
+}
+
+function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) {
+  assert_equals(detectionResult.length, 2, "Number of barcodes");
+  assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
+  assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
+  assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
+  assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
+}
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageBitmap.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageBitmap.html
new file mode 100644
index 0000000..a7157c0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageBitmap.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that a Detector's detect() works on an ImageBitmap.
+const imageBitmapTests =
+    [
+      {
+        createDetector: () => { return new FaceDetector(); },
+        mockTestName: "FaceDetectionTest",
+        detectionResultTest: FaceDetectorDetectionResultTest,
+        name: "Face - detect(ImageBitmap)"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        mockTestName: "BarcodeDetectionTest",
+        detectionResultTest: BarcodeDetectorDetectionResultTest,
+        name: "Barcode - detect(ImageBitmap)",
+      }
+    ];
+
+for (let imageBitmapTest of imageBitmapTests) {
+  detection_test(imageBitmapTest.mockTestName, async (t, detectionTest) => {
+    const img = new Image();
+    const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+    img.src = "/images/green-16x16.png";
+    await imgWatcher.wait_for("load");
+    const imageBitmap = await createImageBitmap(img);
+
+    const detector = imageBitmapTest.createDetector();
+    const detectionResult = await detector.detect(imageBitmap);
+    imageBitmapTest.detectionResultTest(detectionResult, detectionTest);
+  }, imageBitmapTest.name);
+}
+
+function FaceDetectorDetectionResultTest(detectionResult, mockTest) {
+  const imageReceivedByMock = mockTest.MockFaceDetectionProvider().getFrameData();
+  assert_equals(imageReceivedByMock.byteLength, 1024, "Image length");
+  const GREEN_PIXEL = 0xFF00FF00;
+  assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
+  assert_equals(detectionResult.length, 3, "Number of faces");
+}
+
+function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) {
+  assert_equals(detectionResult.length, 2, "Number of barcodes");
+  assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
+  assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
+  assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
+  assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
+}
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageData.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageData.html
new file mode 100644
index 0000000..a74c2af
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageData.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that a Detector's detect() works on an ImageBitmap.
+const imageDataTests =
+    [
+      {
+        createDetector: () => { return new FaceDetector(); },
+        mockTestName: "FaceDetectionTest",
+        detectionResultTest: FaceDetectorDetectionResultTest,
+        name: "Face - detect(ImageData)"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        mockTestName: "BarcodeDetectionTest",
+        detectionResultTest: BarcodeDetectorDetectionResultTest,
+        name: "Barcode - detect(ImageData)"
+      }
+    ];
+
+for (let imageDataTest of imageDataTests) {
+  detection_test(imageDataTest.mockTestName, async (t, detectionTest) => {
+    const img = new Image();
+    const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+    img.src = "/images/green-16x16.png";
+    await imgWatcher.wait_for("load");
+    const canvas = document.createElement("canvas");
+    canvas.getContext("2d").drawImage(img, 0, 0);
+
+    const detector = imageDataTest.createDetector();
+    const detectionResult = await detector.detect(canvas.getContext("2d")
+        .getImageData(0, 0, canvas.width, canvas.height));
+    imageDataTest.detectionResultTest(detectionResult, detectionTest);
+  }, imageDataTest.name);
+}
+
+function FaceDetectorDetectionResultTest(detectionResult, mockTest) {
+  const imageReceivedByMock = mockTest.MockFaceDetectionProvider().getFrameData();
+  assert_equals(imageReceivedByMock.byteLength, 180000,"Image length");
+  const GREEN_PIXEL = 0xFF00FF00;
+  assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
+  assert_equals(detectionResult.length, 3, "Number of faces");
+}
+
+function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) {
+  assert_equals(detectionResult.length, 2, "Number of barcodes");
+  assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
+  assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
+  assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
+  assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
+}
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-getSupportedFormats.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-getSupportedFormats.html
new file mode 100644
index 0000000..4ccb5ab76
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/detection-getSupportedFormats.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="help" href="https://wicg.github.io/shape-detection-api/#dom-barcodedetector-getsupportedformats">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+detection_test('BarcodeDetectionTest', async t => {
+  const result = await BarcodeDetector.getSupportedFormats();
+  assert_equals(result.length, 3, 'Number of supported formats');
+  assert_equals(result[0], 'aztec', 'format 1');
+  assert_equals(result[1], 'data_matrix', 'format 2');
+  assert_equals(result[2], 'qr_code', 'format 3');
+}, 'get supported barcode formats');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-on-worker.worker.js b/third_party/blink/web_tests/external/wpt/shape-detection/detection-on-worker.worker.js
new file mode 100644
index 0000000..6b440af7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/detection-on-worker.worker.js
@@ -0,0 +1,47 @@
+importScripts("/resources/testharness.js");
+importScripts("resources/shapedetection-helpers.js");
+
+'use strict';
+
+// These tests verify that a Detector's detect() works on an
+// ImageBitmap on workers.
+const imageBitmapTests =
+    [
+      {
+        createDetector: () => { return new FaceDetector(); },
+        mockTestName: "FaceDetectionTest",
+        resultSize: 3, // Number of faces
+        detectorType: "Face"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        mockTestName: "BarcodeDetectionTest",
+        resultSize: 2, // Number of barcodes
+        detectorType: "Barcode"
+      }
+    ];
+
+for (let imageBitmapTest of imageBitmapTests) {
+  // ImageBitmap is of transferable type and can be sent to and
+  // tested on worker.
+  detection_test(imageBitmapTest.mockTestName, async (t, detectionTest) => {
+    const img = createTestImage();
+    const theImageBitmap = await createImageBitmap(img);
+    const detector = imageBitmapTest.createDetector();
+    const detectionResult = await detector.detect(theImageBitmap);
+    assert_equals(detectionResult.length, imageBitmapTest.resultSize,
+      `Number of ${imageBitmapTest.detectorType}`);
+  }, `${imageBitmapTest.detectorType} Detector detect(ImageBitmap) on worker`);
+}
+
+function createTestImage() {
+  const image = new OffscreenCanvas(100, 50);
+  const imgctx = image.getContext('2d');
+  imgctx.fillStyle = "#F00";
+  imgctx.fillRect(0, 0, 2, 2);
+  imgctx.fillStyle = "#0F0";
+  imgctx.fillRect(0, 0, 1, 1);
+  return image;
+}
+
+done();
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-options.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-options.html
new file mode 100644
index 0000000..3147557
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/detection-options.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<body>
+<img id="img" src="/images/green-16x16.png"/>
+</body>
+<script>
+
+detection_test("FaceDetectionTest", async (t, detectionTest) => {
+  const img = document.getElementById("img");
+  const mock = detectionTest.MockFaceDetectionProvider();
+
+  const detectorWithDefault = new FaceDetector();
+  let faceDetectionResult = await detectorWithDefault.detect(img);
+  assert_equals(mock.getMaxDetectedFaces(), 10, "default maxDetectedFaces");
+  assert_equals(mock.getFastMode(), false, "default maxDetectedFaces");
+
+  const detectorWithOptions =
+      new FaceDetector({maxDetectedFaces: 7, fastMode: true});
+  faceDetectionResult = await detectorWithOptions.detect(img);
+  assert_equals(mock.getMaxDetectedFaces(), 7, "maxDetectedFaces");
+  assert_equals(mock.getFastMode(), true, "maxDetectedFaces");
+}, "Test that FaceDetectionOptions are correctly propagated");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-security-test.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-security-test.html
new file mode 100644
index 0000000..b3f458e23
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/detection-security-test.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// Detectors should reject undecodable images with an InvalidStateError.
+const badImageTests =
+    [
+      {
+        createDetector: () => { return new FaceDetector(); },
+        name: "Face - detect(broken image)"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        name: "Barcode - detect(broken image)",
+      }
+    ];
+
+for (let badImageTest of badImageTests) {
+  // This test verifies that a Detector will reject an undecodable image.
+  promise_test(async t => {
+    const img = new Image();
+    const error =
+        await detectOnElementAndExpectError(badImageTest.createDetector,
+                                            img, "/images/broken.png");
+    assert_equals(error.name, "InvalidStateError");
+  }, badImageTest.name);
+}
+
+// Detectors should reject undecodable videos with an InvalidStateError.
+const badVideoTests =
+    [
+      {
+        createDetector: () => { return new FaceDetector(); },
+        name: "Face - detect(broken video)"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        name: "Barcode - detect(broken video)"
+      }
+    ];
+
+for (let badVideoTest of badVideoTests) {
+  // This test verifies that a Detector will reject a broken video.
+  promise_test(async t => {
+    const video = document.createElement('video');
+    const error =
+        await detectOnElementAndExpectError(badVideoTest.createDetector,
+                                            video, "garbage.webm");
+    assert_equals(error.name, "InvalidStateError");
+  }, badVideoTest.name);
+}
+
+// Returns a Promise that is resolve()d if detect() is rejected. Needs an input
+// |element| (e.g. an HTMLImageElement or HTMLVideoElement) and a |url| to load.
+function detectOnElementAndExpectError(createDetector, element, url) {
+  return new Promise((resolve, reject) => {
+    const tryDetection = async () => {
+      const detector = createDetector();
+      try {
+        const detectionResult = await detector.detect(element);
+        reject("Promise should have been rejected.");
+      } catch (error) {
+        resolve(error);
+      }
+    };
+    element.onload = tryDetection;
+    element.onerror = tryDetection;
+    element.src = url;
+  });
+};
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detector-same-object.html b/third_party/blink/web_tests/external/wpt/shape-detection/detector-same-object.html
new file mode 100644
index 0000000..52540271
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/detector-same-object.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/shapedetection-helpers.js"></script>
+<script>
+
+// These tests verify that detect()ed Detected{Barcode,Face}'s individual
+// fields are [SameObject].
+const imageDataTests =
+    [
+      {
+        createDetector: () => { return new FaceDetector(); },
+        mockTestName: "FaceDetectionTest",
+        detectionResultTest: CheckDetectedFaceSameObjects,
+        name: "Face - detect(ImageData), [SameObject]"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        mockTestName: "BarcodeDetectionTest",
+        detectionResultTest: CheckDetectedBarcodesSameObjects,
+        name: "Barcode - detect(ImageData), [SameObject]"
+      }
+    ];
+
+for (let imageDataTest of imageDataTests) {
+  detection_test(imageDataTest.mockTestName, async t => {
+    const img = new Image();
+    const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+    img.src = "/images/green-16x16.png";
+    await imgWatcher.wait_for("load");
+    const canvas = document.createElement("canvas");
+    canvas.getContext("2d").drawImage(img, 0, 0);
+
+    const detector = imageDataTest.createDetector();
+    const detectionResult = await detector.detect(canvas.getContext("2d")
+        .getImageData(0, 0, canvas.width, canvas.height));
+    imageDataTest.detectionResultTest(detectionResult);
+  }, imageDataTest.name);
+}
+
+function CheckDetectedFaceSameObjects(detectedFaces) {
+  assert_greater_than(detectedFaces.length, 0, "Number of faces");
+  assert_equals(detectedFaces[0].boundingBox, detectedFaces[0].boundingBox);
+  assert_equals(detectedFaces[0].landmarks, detectedFaces[0].landmarks);
+}
+
+function CheckDetectedBarcodesSameObjects(detectedBarcodes) {
+  assert_greater_than(detectedBarcodes.length, 0, "Number of barcodes");
+  assert_equals(detectedBarcodes[0].rawValue, detectedBarcodes[0].rawValue);
+  assert_equals(detectedBarcodes[0].boundingBox, detectedBarcodes[0].boundingBox);
+  assert_equals(detectedBarcodes[0].format, detectedBarcodes[0].format);
+  assert_equals(detectedBarcodes[0].cornerPoints, detectedBarcodes[0].cornerPoints);
+}
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any-expected.txt b/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any-expected.txt
index da45152..3eeafa7 100644
--- a/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any-expected.txt
@@ -1,5 +1,6 @@
 This is a testharness.js-based test.
-PASS Test shape-detection IDL interface
+Found 53 tests; 50 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idl_test setup
 PASS FaceDetector interface: existence and properties of interface object
 PASS FaceDetector interface object length
 PASS FaceDetector interface object name
@@ -7,6 +8,10 @@
 PASS FaceDetector interface: existence and properties of interface prototype object's "constructor" property
 PASS FaceDetector interface: existence and properties of interface prototype object's @@unscopables property
 PASS FaceDetector interface: operation detect(ImageBitmapSource)
+PASS FaceDetector must be primary interface of faceDetector
+PASS Stringification of faceDetector
+PASS FaceDetector interface: faceDetector must inherit property "detect(ImageBitmapSource)" with the proper type
+PASS FaceDetector interface: calling detect(ImageBitmapSource) on faceDetector with too few arguments must throw TypeError
 FAIL DetectedFace interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function() {
                 new interface_object();
             }" did not throw
@@ -17,6 +22,10 @@
 PASS DetectedFace interface: existence and properties of interface prototype object's @@unscopables property
 PASS DetectedFace interface: attribute boundingBox
 PASS DetectedFace interface: attribute landmarks
+PASS DetectedFace must be primary interface of detectedFace
+PASS Stringification of detectedFace
+PASS DetectedFace interface: detectedFace must inherit property "boundingBox" with the proper type
+PASS DetectedFace interface: detectedFace must inherit property "landmarks" with the proper type
 PASS BarcodeDetector interface: existence and properties of interface object
 PASS BarcodeDetector interface object length
 PASS BarcodeDetector interface object name
@@ -25,6 +34,11 @@
 PASS BarcodeDetector interface: existence and properties of interface prototype object's @@unscopables property
 PASS BarcodeDetector interface: operation getSupportedFormats()
 PASS BarcodeDetector interface: operation detect(ImageBitmapSource)
+PASS BarcodeDetector must be primary interface of barcodeDetector
+PASS Stringification of barcodeDetector
+PASS BarcodeDetector interface: barcodeDetector must inherit property "getSupportedFormats()" with the proper type
+PASS BarcodeDetector interface: barcodeDetector must inherit property "detect(ImageBitmapSource)" with the proper type
+PASS BarcodeDetector interface: calling detect(ImageBitmapSource) on barcodeDetector with too few arguments must throw TypeError
 FAIL DetectedBarcode interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function() {
                 new interface_object();
             }" did not throw
@@ -37,5 +51,11 @@
 PASS DetectedBarcode interface: attribute rawValue
 PASS DetectedBarcode interface: attribute format
 PASS DetectedBarcode interface: attribute cornerPoints
+PASS DetectedBarcode must be primary interface of detectedBarcode
+PASS Stringification of detectedBarcode
+PASS DetectedBarcode interface: detectedBarcode must inherit property "boundingBox" with the proper type
+PASS DetectedBarcode interface: detectedBarcode must inherit property "rawValue" with the proper type
+PASS DetectedBarcode interface: detectedBarcode must inherit property "format" with the proper type
+FAIL DetectedBarcode interface: detectedBarcode must inherit property "cornerPoints" with the proper type Unrecognized type Point2D
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any.js b/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any.js
index ea772689..dab7de99 100644
--- a/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any.js
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any.js
@@ -1,12 +1,54 @@
 // META: script=/resources/WebIDLParser.js
 // META: script=/resources/idlharness.js
+// META: script=/shape-detection/resources/shapedetection-helpers.js
 
 // See: https://wicg.github.io/shape-detection-api/
 
-promise_test(async () => {
-  const idl = await fetch('/interfaces/shape-detection-api.idl').then(r => r.text());
+'use strict';
 
-  const idl_array = new IdlArray();
-  idl_array.add_idls(idl);
-  idl_array.test();
-}, 'Test shape-detection IDL interface');
+idl_test(
+  ['shape-detection-api'],
+  ['dom', 'geometry'],
+  async idl_array => {
+    let faceDetectionTest, barcodeDetectionTest;
+    try {
+      faceDetectionTest =
+          await initialize_detection_tests("FaceDetectionTest");
+      barcodeDetectionTest =
+          await initialize_detection_tests("BarcodeDetectionTest");
+      const img = createTestImage();
+      const theImageBitmap = await createImageBitmap(img);
+
+      self.faceDetector = new FaceDetector();
+      const faceDetectionResult = await faceDetector.detect(theImageBitmap);
+      self.detectedFace = faceDetectionResult[0];
+
+      self.barcodeDetector = new BarcodeDetector();
+      const barcodeDetectionResult =
+          await barcodeDetector.detect(theImageBitmap);
+      self.detectedBarcode = barcodeDetectionResult[0];
+    } catch (e) {
+      // Surfaced in idlharness.js's test_object below.
+    } finally {
+      faceDetectionTest.reset();
+      barcodeDetectionTest.reset();
+    }
+
+    idl_array.add_objects({
+      FaceDetector: ['faceDetector'],
+      DetectedFace: ['detectedFace'],
+      BarcodeDetector: ['barcodeDetector'],
+      DetectedBarcode: ['detectedBarcode']
+    });
+  }
+);
+
+function createTestImage() {
+  const image = new OffscreenCanvas(100, 50);
+  const imgctx = image.getContext('2d');
+  imgctx.fillStyle = "#F00";
+  imgctx.fillRect(0, 0, 2, 2);
+  imgctx.fillStyle = "#0F0";
+  imgctx.fillRect(0, 0, 1, 1);
+  return image;
+}
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any.worker-expected.txt
new file mode 100644
index 0000000..58b8ab3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any.worker-expected.txt
@@ -0,0 +1,40 @@
+This is a testharness.js-based test.
+PASS idl_test setup
+PASS FaceDetector interface: existence and properties of interface object
+PASS FaceDetector interface object length
+PASS FaceDetector interface object name
+PASS FaceDetector interface: existence and properties of interface prototype object
+PASS FaceDetector interface: existence and properties of interface prototype object's "constructor" property
+PASS FaceDetector interface: existence and properties of interface prototype object's @@unscopables property
+PASS FaceDetector interface: operation detect(ImageBitmapSource)
+PASS FaceDetector must be primary interface of faceDetector
+PASS Stringification of faceDetector
+PASS FaceDetector interface: faceDetector must inherit property "detect(ImageBitmapSource)" with the proper type
+PASS FaceDetector interface: calling detect(ImageBitmapSource) on faceDetector with too few arguments must throw TypeError
+PASS DetectedFace interface: existence and properties of interface object
+FAIL DetectedFace must be primary interface of detectedFace assert_own_property: self does not have own property "DetectedFace" expected property "DetectedFace" missing
+PASS Stringification of detectedFace
+FAIL DetectedFace interface: detectedFace must not have property "boundingBox" assert_false: expected false got true
+FAIL DetectedFace interface: detectedFace must not have property "landmarks" assert_false: expected false got true
+PASS BarcodeDetector interface: existence and properties of interface object
+PASS BarcodeDetector interface object length
+PASS BarcodeDetector interface object name
+PASS BarcodeDetector interface: existence and properties of interface prototype object
+PASS BarcodeDetector interface: existence and properties of interface prototype object's "constructor" property
+PASS BarcodeDetector interface: existence and properties of interface prototype object's @@unscopables property
+PASS BarcodeDetector interface: operation getSupportedFormats()
+PASS BarcodeDetector interface: operation detect(ImageBitmapSource)
+PASS BarcodeDetector must be primary interface of barcodeDetector
+PASS Stringification of barcodeDetector
+PASS BarcodeDetector interface: barcodeDetector must inherit property "getSupportedFormats()" with the proper type
+PASS BarcodeDetector interface: barcodeDetector must inherit property "detect(ImageBitmapSource)" with the proper type
+PASS BarcodeDetector interface: calling detect(ImageBitmapSource) on barcodeDetector with too few arguments must throw TypeError
+PASS DetectedBarcode interface: existence and properties of interface object
+FAIL DetectedBarcode must be primary interface of detectedBarcode assert_own_property: self does not have own property "DetectedBarcode" expected property "DetectedBarcode" missing
+PASS Stringification of detectedBarcode
+FAIL DetectedBarcode interface: detectedBarcode must not have property "boundingBox" assert_false: expected false got true
+FAIL DetectedBarcode interface: detectedBarcode must not have property "rawValue" assert_false: expected false got true
+FAIL DetectedBarcode interface: detectedBarcode must not have property "format" assert_false: expected false got true
+FAIL DetectedBarcode interface: detectedBarcode must not have property "cornerPoints" assert_false: expected false got true
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/resources/shapedetection-helpers.js b/third_party/blink/web_tests/external/wpt/shape-detection/resources/shapedetection-helpers.js
new file mode 100644
index 0000000..09cea09c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/resources/shapedetection-helpers.js
@@ -0,0 +1,91 @@
+'use strict';
+
+// These tests rely on the User Agent providing an implementation of
+// platform shape detection backends.
+//
+// In Chromium-based browsers this implementation is provided by a polyfill
+// in order to reduce the amount of test-only code shipped to users. To enable
+// these tests the browser must be run with these options:
+//
+//   --enable-blink-features=MojoJS,MojoJSTest
+
+let loadChromiumResources = Promise.resolve().then(() => {
+  if (!MojoInterfaceInterceptor) {
+    // Do nothing on non-Chromium-based browsers or when the Mojo bindings are
+    // not present in the global namespace.
+    return;
+  }
+
+  const prefix = '/gen/services/shape_detection/public/mojom';
+  let chain = Promise.resolve();
+  [
+    '/gen/layout_test_data/mojo/public/js/mojo_bindings.js',
+    '/gen/mojo/public/mojom/base/big_buffer.mojom.js',
+    '/gen/skia/public/interfaces/image_info.mojom.js',
+    '/gen/skia/public/interfaces/bitmap.mojom.js',
+    '/gen/ui/gfx/geometry/mojo/geometry.mojom.js',
+    `${prefix}/barcodedetection.mojom.js`,
+    `${prefix}/barcodedetection_provider.mojom.js`,
+    `${prefix}/facedetection.mojom.js`,
+    `${prefix}/facedetection_provider.mojom.js`,
+    '/resources/chromium/mock-barcodedetection.js',
+    '/resources/chromium/mock-facedetection.js',
+  ].forEach(path => {
+    // Use importScripts for workers.
+    if (typeof document === 'undefined') {
+      chain = chain.then(() => importScripts(path));
+      return;
+    }
+    let script = document.createElement('script');
+    script.src = path;
+    script.async = false;
+    chain = chain.then(() => new Promise(resolve => {
+      script.onload = () => resolve();
+    }));
+    document.head.appendChild(script);
+  });
+
+  return chain;
+});
+
+/**
+ * @param {String} detectionTestName
+ * name of mock shape detection test interface,
+ * must be the item of ["FaceDetectionTest", "BarcodeDetectionTest"]
+*/
+async function initialize_detection_tests(detectionTestName) {
+  let detectionTest;
+  // Use 'self' for workers.
+  if (typeof document === 'undefined') {
+    if (typeof self[detectionTestName] === 'undefined') {
+      await loadChromiumResources;
+    }
+    detectionTest = new self[detectionTestName]();
+  } else {
+    if (typeof window[detectionTestName] === 'undefined') {
+      await loadChromiumResources;
+    }
+    detectionTest = new window[detectionTestName]();
+  }
+  await detectionTest.initialize();
+  return detectionTest;
+}
+
+function detection_test(detectionTestName, func, name, properties) {
+  promise_test(async t => {
+    let detectionTest = await initialize_detection_tests(detectionTestName);
+    try {
+      await func(t, detectionTest);
+    } finally {
+      await detectionTest.reset();
+    };
+  }, name, properties);
+}
+
+function getArrayBufferFromBigBuffer(bigBuffer) {
+  if (bigBuffer.$tag == mojoBase.mojom.BigBuffer.Tags.bytes) {
+    return new Uint8Array(bigBuffer.bytes).buffer;
+  }
+  return bigBuffer.sharedMemory.bufferHandle.mapBuffer(0,
+      bigBuffer.sharedMemory.size).buffer;
+}
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-cross-origin.sub.html b/third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-cross-origin.sub.html
new file mode 100644
index 0000000..c9d86430
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-cross-origin.sub.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+
+// cross-origin resources
+const IMAGE_URL =
+    "https://{{domains[www1]}}:{{ports[https][0]}}/images/green.png";
+const VIDEO_URL =
+    "https://{{domains[www1]}}:{{ports[https][0]}}/media/white.webm";
+
+const crossOriginTests =
+    [
+      {
+        createDetector: () => { return new FaceDetector(); },
+        detectorType: "FaceDetector"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        detectorType: "BarcodeDetector"
+      }
+    ];
+
+for (let crossOriginTest of crossOriginTests) {
+
+  // Verifies that Detector rejects a cross-origin HTMLImageElement.
+  promise_test(async t => {
+    const img = new Image();
+    const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+    img.src = IMAGE_URL;
+    await imgWatcher.wait_for("load");
+    const detector = crossOriginTest.createDetector();
+    promise_rejects(t, "SecurityError", detector.detect(img));
+  }, crossOriginTest.detectorType
+  + " should reject cross-origin HTMLImageElements with a SecurityError.");
+
+  // Verifies that Detector rejects a cross-origin ImageBitmap.
+  promise_test(async t => {
+    const img = new Image();
+    const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+    img.src = IMAGE_URL;
+    await imgWatcher.wait_for("load");
+    const imgBitmap = await createImageBitmap(img);
+    const detector = crossOriginTest.createDetector();
+    promise_rejects(t, "SecurityError", detector.detect(imgBitmap));
+  }, crossOriginTest.detectorType
+  + " should reject cross-origin ImageBitmaps with a SecurityError.");
+
+  // Verifies that Detector rejects a cross-origin HTMLVideoElement.
+  promise_test(async t => {
+    const video = document.createElement('video');
+    const videoWatcher = new EventWatcher(t, video, ["loadeddata", "error"]);
+    video.src = VIDEO_URL;
+    await videoWatcher.wait_for("loadeddata");
+    const detector = crossOriginTest.createDetector();
+    promise_rejects(t, "SecurityError", detector.detect(video));
+  }, crossOriginTest.detectorType
+  + " should reject cross-origin HTMLVideoElements with a SecurityError.");
+
+}
+
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-empty-input.html b/third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-empty-input.html
new file mode 100644
index 0000000..601c992
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-empty-input.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+
+// This test verifies *Detector.detect() returns an empty list when fed with
+// an empty HTMLImageElement.
+const emptyInputTests =
+    [
+      {
+        createDetector: () => { return new FaceDetector(); },
+        name: "Face - detect(empty)"
+      },
+      {
+        createDetector: () => { return new BarcodeDetector(); },
+        name: "Barcode - detect(empty)"
+      }
+    ];
+
+for (let emptyInputTest of emptyInputTests) {
+  promise_test(async t =>{
+    const img = new Image();
+    const imgWatcher = new EventWatcher(t, img, ["load", "error"]);
+    img.src = "";
+    await imgWatcher.wait_for("error");
+
+    const detector = emptyInputTest.createDetector();
+    const detectionResult = await detector.detect(img);
+    assert_equals(detectionResult.length, 0);
+  }, emptyInputTest.name);
+}
+
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/events/click-after-mousedown-cancel.html b/third_party/blink/web_tests/fast/events/click-after-mousedown-cancel.html
index 22fdce23..4b38b38 100644
--- a/third_party/blink/web_tests/fast/events/click-after-mousedown-cancel.html
+++ b/third_party/blink/web_tests/fast/events/click-after-mousedown-cancel.html
@@ -19,6 +19,8 @@
     // Mousedown on the iframe, but no mouseup.
     eventSender.mouseMoveTo(iframe.offsetLeft + iframe.offsetWidth / 2, iframe.offsetTop + iframe.offsetHeight / 2);
     eventSender.mouseDown(1);
+    // Reset eventSender's internal button state.
+    eventSender.setMouseButtonState(-1, []);
     // Click on the button in the main document.
     eventSender.mouseMoveTo(button.offsetLeft + button.offsetWidth / 2, button.offsetTop + button.offsetHeight / 2);
     eventSender.mouseDown(0);
diff --git a/third_party/blink/web_tests/fast/events/hit-test-counts-expected.txt b/third_party/blink/web_tests/fast/events/hit-test-counts-expected.txt
index e8366d2..f894bd5 100644
--- a/third_party/blink/web_tests/fast/events/hit-test-counts-expected.txt
+++ b/third_party/blink/web_tests/fast/events/hit-test-counts-expected.txt
@@ -34,7 +34,7 @@
 Initial: 0+0 0+0 0+0
 MouseMove: 1+0 1+0 1+0
 MouseDown: 1+1 1+1 1+2
-MouseUp: 0+1 0+1 0+1
+MouseUp: 0+0 0+0 0+1
 Wheel: 0+1 0+1 0+1
 TouchStart: 2+0 2+0 1+1
 TouchMove: 0+0 0+0 0+0
@@ -53,7 +53,7 @@
 Initial: 0+0 0+0 0+0
 MouseMove: 1+0 1+0 0+0
 MouseDown: 1+1 1+2 0+0
-MouseUp: 0+1 0+1 0+0
+MouseUp: 0+0 0+1 0+0
 Wheel: 0+1 0+1 0+0
 TouchStart: 2+0 1+1 1+0
 TouchMove: 0+0 0+0 0+0
diff --git a/third_party/blink/web_tests/fast/events/pointerevents/mouse-pointer-capture-in-iframe.html b/third_party/blink/web_tests/fast/events/pointerevents/mouse-pointer-capture-in-iframe.html
new file mode 100644
index 0000000..341f614
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/pointerevents/mouse-pointer-capture-in-iframe.html
@@ -0,0 +1,117 @@
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/testdriver.js"></script>
+<script src="../../../resources/testdriver-vendor.js"></script>
+<script src="../../../external/wpt/resources/testdriver-actions.js"></script>
+<style>
+iframe {
+  width: 300px;
+  height: 300px;
+  top: 100px;
+  left: 100px;
+  border: 0;
+  position: absolute;
+  background: green;
+}
+#outerFrame {
+  width: 500px;
+  height: 500px;
+  background: blue;
+}
+</style>
+<body id="outerFrame body" onload = "run()">
+<div id='outerFrame'>
+<iframe id='innerFrameElement' srcdoc="
+  <html id='innerFrameDocument'>
+  <body id='innerFrame' style='height:500px; width: 500px; padding: 0; margin: 0;'>
+    <script>
+      top.document.testEventList.forEach(function(eventName) {
+        document.addEventListener(eventName, top.handleEvent);
+      });
+    </script>
+  </body>
+  </html>">
+</iframe>
+</div>
+</body>
+<script>
+var receivedEventList = [];
+function handleEvent(event) {
+  receivedEventList.push(event.target.id + ' received ' + event.type);
+
+  if (event.type == 'pointerdown') {
+    if (document.preventDefaultOnPointerDown) {
+      event.preventDefault();
+    }
+  }
+};
+
+document.testEventList = ['pointerup', 'pointerdown', 'pointermove', 'gotpointercapture', 'lostpointercapture'];
+document.testEventList.forEach(function(eventName) {
+  document.getElementById('outerFrame').addEventListener(eventName, handleEvent);
+});
+
+document.preventDefaultOnPointerDown = false;
+
+function run() {
+  promise_test (async() => {
+      receivedEventList = [];
+      expectedEventList = ["outerFrame received pointermove",
+                           "outerFrame received pointerdown",
+                           "outerFrame received pointermove",
+                           "innerFrame received pointermove",
+                           "outerFrame received pointermove",
+                           "innerFrame received pointermove",
+                           "innerFrame received pointerup"];
+      await new test_driver.Actions()
+                           .pointerMove(50, 50)
+                           .pointerDown()
+                           .pointerMove(60, 60)
+                           .pointerMove(200, 200)
+                           .pointerMove(250, 450)
+                           .pointerMove(200, 200)
+                           .pointerUp()
+                           .send();
+
+      assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+  }, "Mouse down at outer frame and move to inner frame, without set pointer capture.");
+
+  promise_test (async() => {
+      receivedEventList = [];
+      expectedEventList = ["innerFrame received pointermove",
+                           "innerFrame received pointerdown",
+                           "innerFrame received pointermove",
+                           "innerFrameDocument received pointermove",
+                           "innerFrameDocument received pointermove"];
+      await new test_driver.Actions()
+                           .pointerMove(200, 200)
+                           .pointerDown()
+                           .pointerMove(250, 250)
+                           .pointerMove(50, 50)
+                           .pointerMove(450, 450)
+                           .send();
+      assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+  }, "Test the frame capture behavior: Mouse down at inner frame and move to outer frame without set pointer capture.");
+
+  promise_test (async() => {
+      document.preventDefaultOnPointerDown = true;
+      receivedEventList = [];
+      expectedEventList = ["innerFrame received pointermove",
+                           "innerFrame received pointerdown",
+                           "innerFrame received pointermove",
+                           "outerFrame received pointermove",
+                           "outerFrame received pointermove",
+                           "outerFrame received pointerup"];
+      await new test_driver.Actions()
+                           .pointerMove(250, 250)
+                           .pointerDown()
+                           .pointerMove(200, 200)
+                           .pointerMove(50, 50)
+                           .pointerMove(450, 450)
+                           .pointerUp()
+                           .send();
+      assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
+      document.preventDefaultOnPointerDown = false;
+  }, "Mouse down with preventDefault at inner frame and move to outer frame.");
+}
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/shapedetection/shapedetection-creation.html b/third_party/blink/web_tests/fast/shapedetection/shapedetection-creation.html
index fc4ac42..a8894fb 100644
--- a/third_party/blink/web_tests/fast/shapedetection/shapedetection-creation.html
+++ b/third_party/blink/web_tests/fast/shapedetection/shapedetection-creation.html
@@ -3,43 +3,6 @@
 <script src=../../resources/testharnessreport.js></script>
 <script>
 
-// This test verifies that DetectedFace can be created
-test(function() {
-  var detectedFace = new DetectedFace();
-  assert_true(detectedFace instanceof DetectedFace);
-}, 'DetectedFace instance can be created.');
-
-// This test verifies that FaceDetector can be created
-test(function() {
-  var faceDetector = new FaceDetector();
-  assert_true(faceDetector instanceof FaceDetector);
-}, 'FaceDetector instance can be created with no argument.');
-
-// This test verifies that FaceDetector can be created with |maxDetectedFaces|
-// argument
-test(function() {
-  var faceDetector = new FaceDetector({maxDetectedFaces: 5});
-  assert_true(faceDetector instanceof FaceDetector);
-}, 'FaceDetector instance can be created with maxDetectedFaces.');
-
-// This test verifies that FaceDetector can be created with |fastMode| argument
-test(function() {
-  var faceDetector = new FaceDetector({fastMode: true});
-  assert_true(faceDetector instanceof FaceDetector);
-}, 'FaceDetector instance can be created with fastMode.');
-
-// This test verifies that DetectedBarcode can be created
-test(function() {
-  var detectedBarcode = new DetectedBarcode();
-  assert_true(detectedBarcode instanceof DetectedBarcode);
-}, 'DetectedBarcode instance can be created.');
-
-// This test verifies that BarcodeDetector can be created
-test(function() {
-  var barcodeDetector = new BarcodeDetector();
-  assert_true(barcodeDetector instanceof BarcodeDetector);
-}, 'BarcodeDetector instance can be created.');
-
 // This test verifies that DetectedText can be created
 test(function() {
   var detectedText = new DetectedText();
diff --git a/third_party/blink/web_tests/fast/shapedetection/shapedetection-empty-input.html b/third_party/blink/web_tests/fast/shapedetection/shapedetection-empty-input.html
index 11376a8d..145b8d8cc 100644
--- a/third_party/blink/web_tests/fast/shapedetection/shapedetection-empty-input.html
+++ b/third_party/blink/web_tests/fast/shapedetection/shapedetection-empty-input.html
@@ -3,32 +3,24 @@
 <script src=../../resources/testharnessreport.js></script>
 <script>
 
-// This test verifies *Detector.detect() returns an empty list when fed with
+// This test verifies TextDetector.detect() returns an empty list when fed with
 // an empty HTMLImageElement.
-var createTestForEmptyInput = function(createDetector) {
-  async_test(function(t) {
-    var image = new Image();
-    var detector = createDetector();
-    var tryDetection = function() {
-      detector.detect(image)
-          .then(detectionResult => {
-            assert_equals(detectionResult.length, 0);
-            t.done();
-          })
-          .catch(error => {
-            assert_unreached("detect() rejected with error: " + error)
-          });
-    };
-    image.onload = tryDetection;
-    image.onerror = tryDetection;
-    image.src = "";
-  });
-};
-
-generate_tests(createTestForEmptyInput, [
-  [ "Face - detect(empty)", () => { return new FaceDetector(); } ],
-  [ "Barcode - detect(empty)", () => { return new BarcodeDetector(); } ],
-  [ "Text - detect(empty)", () => { return new TextDetector(); } ]
-]);
+async_test(function(t) {
+  var image = new Image();
+  var detector = new TextDetector();
+  var tryDetection = function() {
+    detector.detect(image)
+        .then(detectionResult => {
+          assert_equals(detectionResult.length, 0);
+          t.done();
+        })
+        .catch(error => {
+          assert_unreached("detect() rejected with error: " + error)
+        });
+  };
+  image.onload = tryDetection;
+  image.onerror = tryDetection;
+  image.src = "";
+}, "Text - detect(empty)");
 
 </script>
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/animation-overlap-with-children-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/animation-overlap-with-children-expected.txt
index 109c3b7..98b02b1 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/animation-overlap-with-children-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/animation-overlap-with-children-expected.txt
@@ -12,21 +12,21 @@
     {
       "name": "LayoutBlockFlow DIV id='to-animate' class='container animating'",
       "bounds": [262, 212],
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow DIV class='composited banner'",
       "bounds": [250, 50],
       "contentsOpaque": true,
       "backgroundColor": "#C0C0C0",
-      "transform": 2
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='test1 box'",
       "position": [11, 21],
       "bounds": [220, 150],
       "backgroundColor": "#FFA500",
-      "transform": 1
+      "transform": 2
     }
   ],
   "transforms": [
@@ -46,6 +46,16 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [0, 0, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [6, 6, 0, 1]
       ]
     }
diff --git a/third_party/blink/web_tests/http/tests/shapedetection/shapedetection-cross-origin.html b/third_party/blink/web_tests/http/tests/shapedetection/shapedetection-cross-origin.html
index 534760e1..16956f79 100644
--- a/third_party/blink/web_tests/http/tests/shapedetection/shapedetection-cross-origin.html
+++ b/third_party/blink/web_tests/http/tests/shapedetection/shapedetection-cross-origin.html
@@ -9,34 +9,34 @@
 
 // Returns a Promise that is resolve()d if detect() is rejected. Needs an input
 // |element| (e.g. an HTMLImageElement or HTMLVideoElement) and a |url| to load.
-function detectFaceOnElementAndExpectError(element, url) {
+function detectTextOnElementAndExpectError(element, url) {
   return new Promise(function(resolve, reject) {
-    var tryFaceDetection = function() {
-      var faceDetector = new FaceDetector();
-      faceDetector.detect(element)
-          .then(faceDetectionResult => {
+    var tryTextDetection = function() {
+      var textDetector = new TextDetector();
+      textDetector.detect(element)
+          .then(textDetectionResult => {
             reject("Promise should have been rejected.");
           })
           .catch(error => {
             resolve(error);
           });
     };
-    element.onload = tryFaceDetection;
-    element.onerror = tryFaceDetection;
+    element.onload = tryTextDetection;
+    element.onerror = tryTextDetection;
     element.src = url;
   });
 }
 
-function detectFaceOnImageBitmapAndExpectError(imageUrl) {
+function detectTextOnImageBitmapAndExpectError(imageUrl) {
   return new Promise(function(resolve, reject) {
     var image = new Image();
     image.onload = function() {
       createImageBitmap(image)
           .then(imageBitmap => {
-            var faceDetector = new FaceDetector();
-            return faceDetector.detect(imageBitmap);
+            var textDetector = new TextDetector();
+            return textDetector.detect(imageBitmap);
           })
-          .then(faceDetectionResult => {
+          .then(textDetectionResult => {
             reject("Promise should have been rejected.");
           })
           .catch(error => {
@@ -48,33 +48,33 @@
   });
 }
 
-// Verifies that FaceDetector rejects a cross-origin HTMLImageElement.
+// Verifies that TextDetector rejects a cross-origin HTMLImageElement.
 promise_test(function(t) {
   var image = new Image();
-  return detectFaceOnElementAndExpectError(image, IMAGE_URL)
+  return detectTextOnElementAndExpectError(image, IMAGE_URL)
       .then(error => {
         assert_equals(error.name, "SecurityError");
       });
 },
-"FaceDetector should reject cross-origin HTMLImageElements with a SecurityError.");
+"TextDetector should reject cross-origin HTMLImageElements with a SecurityError.");
 
-// Verifies that FaceDetector rejects a cross-origin ImageBitmap.
+// Verifies that TextDetector rejects a cross-origin ImageBitmap.
 promise_test(function(t) {
-  return detectFaceOnImageBitmapAndExpectError(IMAGE_URL)
+  return detectTextOnImageBitmapAndExpectError(IMAGE_URL)
       .then(error => {
         assert_equals(error.name, "SecurityError");
       });
 },
-"FaceDetector should reject cross-origin ImageBitmaps with a SecurityError.");
+"TextDetector should reject cross-origin ImageBitmaps with a SecurityError.");
 
-// Verifies that FaceDetector rejects a cross-origin HTMLVideoElement.
+// Verifies that TextDetector rejects a cross-origin HTMLVideoElement.
 promise_test(function(t) {
   var video = document.createElement('video');
-  return detectFaceOnElementAndExpectError(video, VIDEO_URL)
+  return detectTextOnElementAndExpectError(video, VIDEO_URL)
       .then(error => {
         assert_equals(error.name, "SecurityError");
       });
 },
-"FaceDetector should reject cross-origin HTMLVideoElements with a SecurityError.");
+"TextDetector should reject cross-origin HTMLVideoElements with a SecurityError.");
 
 </script>
diff --git a/third_party/blink/web_tests/shapedetection/detected-boundingBox-read-only.html b/third_party/blink/web_tests/shapedetection/detected-boundingBox-read-only.html
index 2529e73..00f91b0d 100644
--- a/third_party/blink/web_tests/shapedetection/detected-boundingBox-read-only.html
+++ b/third_party/blink/web_tests/shapedetection/detected-boundingBox-read-only.html
@@ -2,12 +2,8 @@
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection_provider.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection_provider.mojom.js"></script>
 <script src="file:///gen/services/shape_detection/public/mojom/textdetection.mojom.js"></script>
 <script src="resources/big-buffer-helpers.js"></script>
-<script src="resources/mock-barcodedetection.js"></script>
-<script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
 <script>
 
@@ -45,21 +41,9 @@
   });
 }
 
-// These tests verify that detected{Face, Barcode, Text}'s boundingBox should be DOMRectReadOnly.
+// These tests verify that detected Text's boundingBox should be DOMRectReadOnly.
 generate_tests(createTestForImageData, [
   [
-    "Face - detectedFace.boundingBox should be DOMRectReadOnly",
-    () => { return new FaceDetector(); },
-    mockFaceDetectionProvider,
-    CheckDetectedReadOnlyBoundingBoxes
-  ],
-  [
-    "Barcode - detectedBarcode.boundingBox should be DOMRectReadOnly",
-    () => { return new BarcodeDetector(); },
-    mockBarcodeDetectionProvider,
-    CheckDetectedReadOnlyBoundingBoxes
-  ],
-  [
     "Text - detectedText.boundingBox should be DOMRectReadOnly",
     () => { return new TextDetector(); },
     mockTextDetection,
diff --git a/third_party/blink/web_tests/shapedetection/detection-HTMLCanvasElement.html b/third_party/blink/web_tests/shapedetection/detection-HTMLCanvasElement.html
index 941bed8..b966923 100644
--- a/third_party/blink/web_tests/shapedetection/detection-HTMLCanvasElement.html
+++ b/third_party/blink/web_tests/shapedetection/detection-HTMLCanvasElement.html
@@ -3,14 +3,8 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/mojo/public/mojom/base/big_buffer.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection_provider.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection_provider.mojom.js"></script>
 <script src="file:///gen/services/shape_detection/public/mojom/textdetection.mojom.js"></script>
 <script src="resources/big-buffer-helpers.js"></script>
-<script src="resources/mock-barcodedetection.js"></script>
-<script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
 <script>
 
@@ -53,22 +47,6 @@
   });
 };
 
-function FaceDetectorDetectionResultTest(detectionResult, mock) {
-  const imageReceivedByMock = mock.getFrameData();
-  assert_equals(imageReceivedByMock.byteLength, 180000,"Image length");
-  const GREEN_PIXEL = 0xFF00FF00;
-  assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
-  assert_equals(detectionResult.length, 3, "Number of faces");
-}
-
-function BarcodeDetectorDetectionResultTest(detectionResult, mock) {
-  assert_equals(detectionResult.length, 2, "Number of barcodes");
-  assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
-  assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
-  assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
-  assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
-}
-
 function TextDetectorDetectionResultTest(detectionResult, mock) {
   assert_equals(detectionResult.length, 2, "Number of textBlocks");
   assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1");
@@ -77,37 +55,9 @@
 
 // These tests verify that a Detector's detect() works on an HTMLCanvasElement
 // and on an OffscreenCanvas. Use the mock mojo server implemented in
-// mock-{barcode,face}detection.js.
+// mock-textdetection.js.
 generate_tests(createTestForCanvasElement, [
   [
-    "Face - detect(HTMLCanvasElement)",
-    () => { return new FaceDetector(); },
-    () => { return document.createElement("canvas"); },
-    mockFaceDetectionProvider,
-    FaceDetectorDetectionResultTest
-  ],
-  [
-    "Face - detect(OffscreenCanvas)",
-    () => { return new FaceDetector(); },
-    () => { return new OffscreenCanvas(300, 150); },
-    mockFaceDetectionProvider,
-    FaceDetectorDetectionResultTest
-  ],
-  [
-    "Barcode - detect(HTMLCanvasElement)",
-    () => { return new BarcodeDetector(); },
-    () => { return document.createElement("canvas"); },
-    mockBarcodeDetectionProvider,
-    BarcodeDetectorDetectionResultTest
-  ],
-  [
-    "Barcode - detect(OffscreenCanvas)",
-    () => { return new BarcodeDetector(); },
-    () => { return new OffscreenCanvas(300, 150); },
-    mockBarcodeDetectionProvider,
-    BarcodeDetectorDetectionResultTest
-  ],
-  [
     "Text - detect(HTMLCanvasElement)",
     () => { return new TextDetector(); },
     () => { return document.createElement("canvas"); },
diff --git a/third_party/blink/web_tests/shapedetection/detection-HTMLImageElement.html b/third_party/blink/web_tests/shapedetection/detection-HTMLImageElement.html
index 5f68203..2095280 100644
--- a/third_party/blink/web_tests/shapedetection/detection-HTMLImageElement.html
+++ b/third_party/blink/web_tests/shapedetection/detection-HTMLImageElement.html
@@ -3,14 +3,8 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/mojo/public/mojom/base/big_buffer.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection_provider.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection_provider.mojom.js"></script>
 <script src="file:///gen/services/shape_detection/public/mojom/textdetection.mojom.js"></script>
 <script src="resources/big-buffer-helpers.js"></script>
-<script src="resources/mock-barcodedetection.js"></script>
-<script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
 <body>
 <img id="img" src="../media/content/greenbox.png"/>
@@ -32,33 +26,6 @@
   });
 };
 
-function FaceDetectorDetectionResultTest(detectionResult, mock) {
-  const imageReceivedByMock = mock.getFrameData();
-  assert_equals(imageReceivedByMock.byteLength, 2500, "Image length");
-  const GREEN_PIXEL = 0xFF00FF00;
-  assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
-  assert_equals(detectionResult.length, 3, "Number of faces");
-  assert_equals(detectionResult[0].landmarks.length, 2, "Number of landmarks");
-  assert_object_equals(detectionResult[0].landmarks[0],
-                      {type : 'eye', locations : [{x : 4.0, y : 5.0}]},
-                      "landmark #1");
-  assert_equals(detectionResult[0].landmarks[1].locations.length, 8,
-                "Number of locations along eye");
-  assert_object_equals(detectionResult[1].landmarks[0],
-                      {type : 'nose', locations : [{x : 100.0, y : 50.0}]},
-                      "landmark #2");
-  assert_equals(detectionResult[1].landmarks[1].locations.length, 9,
-                "Number of locations along nose");
-}
-
-function BarcodeDetectorDetectionResultTest(detectionResult, mock) {
-  assert_equals(detectionResult.length, 2, "Number of barcodes");
-  assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
-  assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
-  assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
-  assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
-}
-
 function TextDetectorDetectionResultTest(detectionResult, mock) {
   assert_equals(detectionResult.length, 2, "Number of textBlocks");
   assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1");
@@ -74,21 +41,9 @@
 }
 
 // These tests verify that a Detector's detect() works on an HTMLImageElement.
-// Use the mock mojo server implemented in mock-{barcode,face}detection.js.
+// Use the mock mojo server implemented in mock-textdetection.js.
 generate_tests(createTestForImageElement, [
   [
-    "Face - detect(HTMLImageElement)",
-    () => { return new FaceDetector(); },
-    mockFaceDetectionProvider,
-    FaceDetectorDetectionResultTest
-  ],
-  [
-    "Barcode - detect(HTMLImageElement)",
-    () => { return new BarcodeDetector(); },
-    mockBarcodeDetectionProvider,
-    BarcodeDetectorDetectionResultTest
-  ],
-  [
     "Text - detect(HTMLImageElement)",
     () => { return new TextDetector(); },
     mockTextDetection,
diff --git a/third_party/blink/web_tests/shapedetection/detection-HTMLVideoElement.html b/third_party/blink/web_tests/shapedetection/detection-HTMLVideoElement.html
index 682b0921..8831611a 100644
--- a/third_party/blink/web_tests/shapedetection/detection-HTMLVideoElement.html
+++ b/third_party/blink/web_tests/shapedetection/detection-HTMLVideoElement.html
@@ -3,14 +3,8 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/mojo/public/mojom/base/big_buffer.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection_provider.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection_provider.mojom.js"></script>
 <script src="file:///gen/services/shape_detection/public/mojom/textdetection.mojom.js"></script>
 <script src="resources/big-buffer-helpers.js"></script>
-<script src="resources/mock-barcodedetection.js"></script>
-<script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
 <script>
 
@@ -37,20 +31,6 @@
   });
 };
 
-function FaceDetectorDetectionResultTest(detectionResult, mock) {
-  const imageReceivedByMock = mock.getFrameData();
-  assert_equals(imageReceivedByMock.byteLength, 307200, "Image length");
-  assert_equals(detectionResult.length, 3, "Number of faces");
-}
-
-function BarcodeDetectorDetectionResultTest(detectionResult, mock) {
-  assert_equals(detectionResult.length, 2, "Number of barcodes");
-  assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
-  assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
-  assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
-  assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
-}
-
 function TextDetectorDetectionResultTest(detectionResult, mock) {
   assert_equals(detectionResult.length, 2, "Number of textBlocks");
   assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1");
@@ -58,21 +38,9 @@
 }
 
 // These tests verify that a Detector's detect() works on an HTMLVideoElement.
-// Use the mock mojo server implemented in mock-{barcode,face}detection.js.
+// Use the mock mojo server implemented in mock-textdetection.js.
 generate_tests(createTestForVideoElement, [
   [
-    "Face - detect(HTMLVideoElement)",
-    () => { return new FaceDetector(); },
-    mockFaceDetectionProvider,
-    FaceDetectorDetectionResultTest
-  ],
-  [
-    "Barcode - detect(HTMLVideoElement)",
-    () => { return new BarcodeDetector(); },
-    mockBarcodeDetectionProvider,
-    BarcodeDetectorDetectionResultTest
-  ],
-  [
     "Text - detect(HTMLVideoElement)",
     () => { return new TextDetector(); },
     mockTextDetection,
diff --git a/third_party/blink/web_tests/shapedetection/detection-ImageBitmap.html b/third_party/blink/web_tests/shapedetection/detection-ImageBitmap.html
index 4f64ac72..a441421 100644
--- a/third_party/blink/web_tests/shapedetection/detection-ImageBitmap.html
+++ b/third_party/blink/web_tests/shapedetection/detection-ImageBitmap.html
@@ -3,14 +3,8 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/mojo/public/mojom/base/big_buffer.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection_provider.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection_provider.mojom.js"></script>
 <script src="file:///gen/services/shape_detection/public/mojom/textdetection.mojom.js"></script>
 <script src="resources/big-buffer-helpers.js"></script>
-<script src="resources/mock-barcodedetection.js"></script>
-<script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
 <script>
 
@@ -35,22 +29,6 @@
   });
 };
 
-function FaceDetectorDetectionResultTest(detectionResult, mock) {
-  const imageReceivedByMock = mock.getFrameData();
-  assert_equals(imageReceivedByMock.byteLength, 2500,"Image length");
-  const GREEN_PIXEL = 0xFF00FF00;
-  assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
-  assert_equals(detectionResult.length, 3, "Number of faces");
-}
-
-function BarcodeDetectorDetectionResultTest(detectionResult, mock) {
-  assert_equals(detectionResult.length, 2, "Number of barcodes");
-  assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
-  assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
-  assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
-  assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
-}
-
 function TextDetectorDetectionResultTest(detectionResult, mock) {
   assert_equals(detectionResult.length, 2, "Number of textBlocks");
   assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1");
@@ -58,21 +36,9 @@
 }
 
 // These tests verify that a Detector's detect() works on an ImageBitmap.
-// Use the mock mojo server implemented in mock-{barcode,face}detection.js.
+// Use the mock mojo server implemented in mock-textdetection.js.
 generate_tests(createTestForImageBitmap, [
   [
-    "Face - detect(ImageBitmap)",
-    () => { return new FaceDetector(); },
-    mockFaceDetectionProvider,
-    FaceDetectorDetectionResultTest
-  ],
-  [
-    "Barcode - detect(ImageBitmap)",
-    () => { return new BarcodeDetector(); },
-    mockBarcodeDetectionProvider,
-    BarcodeDetectorDetectionResultTest
-  ],
-  [
     "Text - detect(ImageBitmap)",
     () => { return new TextDetector(); },
     mockTextDetection,
diff --git a/third_party/blink/web_tests/shapedetection/detection-ImageData.html b/third_party/blink/web_tests/shapedetection/detection-ImageData.html
index 4f9d102..0168fe1 100644
--- a/third_party/blink/web_tests/shapedetection/detection-ImageData.html
+++ b/third_party/blink/web_tests/shapedetection/detection-ImageData.html
@@ -3,14 +3,8 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/mojo/public/mojom/base/big_buffer.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection_provider.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection_provider.mojom.js"></script>
 <script src="file:///gen/services/shape_detection/public/mojom/textdetection.mojom.js"></script>
 <script src="resources/big-buffer-helpers.js"></script>
-<script src="resources/mock-barcodedetection.js"></script>
-<script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
 <script>
 
@@ -39,22 +33,6 @@
   });
 };
 
-function FaceDetectorDetectionResultTest(detectionResult, mock) {
-  const imageReceivedByMock = mock.getFrameData();
-  assert_equals(imageReceivedByMock.byteLength, 180000,"Image length");
-  const GREEN_PIXEL = 0xFF00FF00;
-  assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
-  assert_equals(detectionResult.length, 3, "Number of faces");
-}
-
-function BarcodeDetectorDetectionResultTest(detectionResult, mock) {
-  assert_equals(detectionResult.length, 2, "Number of barcodes");
-  assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
-  assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format");
-  assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
-  assert_equals(detectionResult[1].format, "code_128", "barcode 2 format");
-}
-
 function TextDetectorDetectionResultTest(detectionResult, mock) {
   assert_equals(detectionResult.length, 2, "Number of textBlocks");
   assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1");
@@ -62,21 +40,9 @@
 }
 
 // These tests verify that a Detector's detect() works on an ImageBitmap. Use
-// the mock mojo server implemented in mock-{barcode,face}detection.js.
+// the mock mojo server implemented in mock-textdetection.js.
 generate_tests(createTestForImageData, [
   [
-    "Face - detect(ImageData)",
-    () => { return new FaceDetector(); },
-    mockFaceDetectionProvider,
-    FaceDetectorDetectionResultTest
-  ],
-  [
-    "Barcode - detect(ImageData)",
-    () => { return new BarcodeDetector(); },
-    mockBarcodeDetectionProvider,
-    BarcodeDetectorDetectionResultTest
-  ],
-  [
     "Text - detect(ImageData)",
     () => { return new TextDetector(); },
     mockTextDetection,
diff --git a/third_party/blink/web_tests/shapedetection/detection-on-worker.html b/third_party/blink/web_tests/shapedetection/detection-on-worker.html
index 0031ff2..800c64d 100644
--- a/third_party/blink/web_tests/shapedetection/detection-on-worker.html
+++ b/third_party/blink/web_tests/shapedetection/detection-on-worker.html
@@ -26,19 +26,9 @@
 };
 
 // These tests verify that a Detector's detect() works on an ImageBitmap on
-// workers. Use the mock mojo server implemented in mock-*detection.js.
+// workers. Use the mock mojo server implemented in mock-textdetection.js.
 generate_tests(createTestForImageBitmap, [
   [
-    "Face",
-    "Face",
-    3 // Number of faces
-  ],
-  [
-    "Barcode",
-    "Barcode",
-    2 // Number of barcodes
-  ],
-  [
     "Text",
     "Text",
     2 // Number of text blocks
diff --git a/third_party/blink/web_tests/shapedetection/detection-options.html b/third_party/blink/web_tests/shapedetection/detection-options.html
deleted file mode 100644
index 557043e..0000000
--- a/third_party/blink/web_tests/shapedetection/detection-options.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/mojo/public/mojom/base/big_buffer.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection_provider.mojom.js"></script>
-<script src="resources/big-buffer-helpers.js"></script>
-<script src="resources/mock-facedetection.js"></script>
-<body>
-<img id="img" src="../media/content/greenbox.png"/>
-</body>
-<script>
-promise_test(async function() {
-  var mock = mockFaceDetectionProvider;
-  var img = document.getElementById("img");
-
-  var detectorWithDefault = new FaceDetector();
-  var faceDetectionResult = await detectorWithDefault.detect(img);
-  assert_equals(mock.getMaxDetectedFaces(), 10, "default maxDetectedFaces");
-  assert_equals(mock.getFastMode(), false, "default maxDetectedFaces");
-
-  var detectorWithOptions =
-      new FaceDetector({maxDetectedFaces: 7, fastMode: true});
-  faceDetectionResult = await detectorWithOptions.detect(img);
-  assert_equals(mock.getMaxDetectedFaces(), 7, "maxDetectedFaces");
-  assert_equals(mock.getFastMode(), true, "maxDetectedFaces");
-}, "Test that FaceDetectionOptions are correctly propagated");
-</script>
diff --git a/third_party/blink/web_tests/shapedetection/detection-security-test.html b/third_party/blink/web_tests/shapedetection/detection-security-test.html
index c43802e..baf8a82 100644
--- a/third_party/blink/web_tests/shapedetection/detection-security-test.html
+++ b/third_party/blink/web_tests/shapedetection/detection-security-test.html
@@ -3,14 +3,8 @@
 <script src=../resources/testharnessreport.js></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/mojo/public/mojom/base/big_buffer.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection_provider.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection_provider.mojom.js"></script>
 <script src="file:///gen/services/shape_detection/public/mojom/textdetection.mojom.js"></script>
 <script src="resources/big-buffer-helpers.js"></script>
-<script src="resources/mock-barcodedetection.js"></script>
-<script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
 <script>
 
@@ -47,8 +41,6 @@
 
 // Detectors should reject undecodable images with an InvalidStateError.
 generate_tests(createTestForBadImage, [
-  [ "Face - detect(broken image)", () => { return new FaceDetector(); } ],
-  [ "Barcode - detect(broken image)", () => { return new BarcodeDetector(); } ],
   [ "Text - detect(broken image)", () => { return new TextDetector(); } ]
 ]);
 
@@ -66,8 +58,6 @@
 
 // Detectors should reject undecodable videos with an InvalidStateError.
 generate_tests(createTestForBadVideo, [
-  [ "Face - detect(broken video)", () => { return new FaceDetector(); } ],
-  [ "Barcode - detect(broken video)", () => { return new BarcodeDetector(); } ],
   [ "Text - detect(broken video)", () => { return new TextDetector(); } ]
 ]);
 
diff --git a/third_party/blink/web_tests/shapedetection/detection-support.html b/third_party/blink/web_tests/shapedetection/detection-support.html
deleted file mode 100644
index f8968b07..0000000
--- a/third_party/blink/web_tests/shapedetection/detection-support.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<link rel="help" href="https://wicg.github.io/shape-detection-api/#dom-barcodedetector-getsupportedformats">
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection_provider.mojom.js"></script>
-<script src="resources/mock-barcodedetection.js"></script>
-<script>
-'use strict';
-
-promise_test(() => {
-  return new Promise((resolve, reject) => {
-    var formats = BarcodeDetector.getSupportedFormats();
-    resolve(formats);
-  }).then(result => {
-    assert_equals(result.length, 3, 'Number of supported formats');
-    assert_equals(result[0], 'aztec', 'format 1');
-    assert_equals(result[1], 'data_matrix', 'format 2');
-    assert_equals(result[2], 'qr_code', 'format 3');
-  });
-}, 'get supported barcode formats');
-
-</script>
diff --git a/third_party/blink/web_tests/shapedetection/detector-same-object.html b/third_party/blink/web_tests/shapedetection/detector-same-object.html
index 6d595fd..6dc139f5 100644
--- a/third_party/blink/web_tests/shapedetection/detector-same-object.html
+++ b/third_party/blink/web_tests/shapedetection/detector-same-object.html
@@ -3,14 +3,8 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/mojo/public/mojom/base/big_buffer.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/barcodedetection_provider.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection.mojom.js"></script>
-<script src="file:///gen/services/shape_detection/public/mojom/facedetection_provider.mojom.js"></script>
 <script src="file:///gen/services/shape_detection/public/mojom/textdetection.mojom.js"></script>
 <script src="resources/big-buffer-helpers.js"></script>
-<script src="resources/mock-barcodedetection.js"></script>
-<script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
 <script>
 
@@ -38,42 +32,16 @@
   });
 };
 
-function CheckDetectedFaceSameObjects(detectedFaces) {
-  assert_greater_than(detectedFaces.length, 0, "Number of faces");
-  assert_equals(detectedFaces[0].boundingBox, detectedFaces[0].boundingBox);
-  assert_equals(detectedFaces[0].landmarks, detectedFaces[0].landmarks);
-}
-
-function CheckDetectedBarcodesSameObjects(detectedBarcodes) {
-  assert_greater_than(detectedBarcodes.length, 0, "Number of barcodes");
-  assert_equals(detectedBarcodes[0].rawValue, detectedBarcodes[0].rawValue);
-  assert_equals(detectedBarcodes[0].boundingBox, detectedBarcodes[0].boundingBox);
-  assert_equals(detectedBarcodes[0].format, detectedBarcodes[0].format);
-  assert_equals(detectedBarcodes[0].cornerPoints, detectedBarcodes[0].cornerPoints);
-}
-
 function CheckDetectedTextBlocksSameObjects(detectedTextBlocks) {
   assert_greater_than(detectedTextBlocks.length, 0, "Number of textBlocks");
   assert_equals(detectedTextBlocks[0].rawValue, detectedTextBlocks[0].rawValue);
   assert_equals(detectedTextBlocks[0].boundingBox, detectedTextBlocks[0].boundingBox);
 }
 
-// These tests verify that detect()ed Detected{Barcode,Face,Text}'s individual
+// These tests verify that detect()ed Detected Text's individual
 // fields are [SameObject].
 generate_tests(createTestForImageData, [
   [
-    "Face - detect(ImageData), [SameObject]",
-    () => { return new FaceDetector(); },
-    mockFaceDetectionProvider,
-    CheckDetectedFaceSameObjects
-  ],
-  [
-    "Barcode - detect(ImageData), [SameObject]",
-    () => { return new BarcodeDetector(); },
-    mockBarcodeDetectionProvider,
-    CheckDetectedBarcodesSameObjects
-  ],
-  [
     "Text - detect(ImageData), [SameObject]",
     () => { return new TextDetector(); },
     mockTextDetection,
diff --git a/third_party/blink/web_tests/shapedetection/resources/mock-facedetection.js b/third_party/blink/web_tests/shapedetection/resources/mock-facedetection.js
deleted file mode 100644
index 1971e0f5..0000000
--- a/third_party/blink/web_tests/shapedetection/resources/mock-facedetection.js
+++ /dev/null
@@ -1,84 +0,0 @@
-"use strict";
-
-class MockFaceDetectionProvider {
-  constructor() {
-    this.bindingSet_ = new mojo.BindingSet(
-        shapeDetection.mojom.FaceDetectionProvider);
-
-    this.interceptor_ = new MojoInterfaceInterceptor(
-        shapeDetection.mojom.FaceDetectionProvider.name);
-    this.interceptor_.oninterfacerequest =
-        e => this.bindingSet_.addBinding(this, e.handle);
-    this.interceptor_.start();
-  }
-
-  createFaceDetection(request, options) {
-    this.mockService_ = new MockFaceDetection(request, options);
-  }
-
-  getFrameData() {
-    return this.mockService_.bufferData_;
-  }
-
-  getMaxDetectedFaces() {
-   return this.mockService_.maxDetectedFaces_;
-  }
-
-  getFastMode () {
-    return this.mockService_.fastMode_;
-  }
-}
-
-class MockFaceDetection {
-  constructor(request, options) {
-    this.maxDetectedFaces_ = options.maxDetectedFaces;
-    this.fastMode_ = options.fastMode;
-    this.binding_ =
-        new mojo.Binding(shapeDetection.mojom.FaceDetection, this, request);
-  }
-
-  detect(bitmapData) {
-    this.bufferData_ =
-        new Uint32Array(getArrayBufferFromBigBuffer(bitmapData.pixelData));
-    return Promise.resolve({
-      results: [
-        {
-          boundingBox: {x: 1.0, y: 1.0, width: 100.0, height: 100.0},
-          landmarks: [{
-            type: shapeDetection.mojom.LandmarkType.EYE,
-            locations: [{x: 4.0, y: 5.0}]
-          },
-          {
-            type: shapeDetection.mojom.LandmarkType.EYE,
-            locations: [
-              {x: 4.0, y: 5.0}, {x: 5.0, y: 4.0}, {x: 6.0, y: 3.0},
-              {x: 7.0, y: 4.0}, {x: 8.0, y: 5.0}, {x: 7.0, y: 6.0},
-              {x: 6.0, y: 7.0}, {x: 5.0, y: 6.0}
-            ]
-          }]
-        },
-        {
-          boundingBox: {x: 2.0, y: 2.0, width: 200.0, height: 200.0},
-          landmarks: [{
-            type: shapeDetection.mojom.LandmarkType.NOSE,
-            locations: [{x: 100.0, y: 50.0}]
-          },
-          {
-            type: shapeDetection.mojom.LandmarkType.NOSE,
-            locations: [
-              {x: 80.0, y: 50.0}, {x: 70.0, y: 60.0}, {x: 60.0, y: 70.0},
-              {x: 70.0, y: 60.0}, {x: 80.0, y: 70.0}, {x: 90.0, y: 80.0},
-              {x: 100.0, y: 70.0}, {x: 90.0, y: 60.0}, {x: 80.0, y: 50.0}
-            ]
-          }]
-        },
-        {
-          boundingBox: {x: 3.0, y: 3.0, width: 300.0, height: 300.0},
-          landmarks: []
-        },
-      ]
-    });
-  }
-}
-
-let mockFaceDetectionProvider = new MockFaceDetectionProvider();
diff --git a/third_party/blink/web_tests/shapedetection/resources/worker.js b/third_party/blink/web_tests/shapedetection/resources/worker.js
index badd55b..60fab7cd 100644
--- a/third_party/blink/web_tests/shapedetection/resources/worker.js
+++ b/third_party/blink/web_tests/shapedetection/resources/worker.js
@@ -4,14 +4,8 @@
 importScripts("file:///gen/skia/public/interfaces/image_info.mojom.js");
 importScripts("file:///gen/skia/public/interfaces/bitmap.mojom.js");
 importScripts("file:///gen/ui/gfx/geometry/mojo/geometry.mojom.js");
-importScripts("file:///gen/services/shape_detection/public/mojom/barcodedetection.mojom.js");
-importScripts("file:///gen/services/shape_detection/public/mojom/barcodedetection_provider.mojom.js");
-importScripts("file:///gen/services/shape_detection/public/mojom/facedetection.mojom.js");
-importScripts("file:///gen/services/shape_detection/public/mojom/facedetection_provider.mojom.js");
 importScripts("file:///gen/services/shape_detection/public/mojom/textdetection.mojom.js");
 importScripts("big-buffer-helpers.js");
-importScripts("mock-barcodedetection.js");
-importScripts("mock-facedetection.js");
 importScripts("mock-textdetection.js");
 
 onmessage = async function(e) {
diff --git a/third_party/blink/web_tests/storage/websql/fts-pointer-leak-742407.html b/third_party/blink/web_tests/storage/websql/fts-pointer-leak-742407.html
index 95add45..dc722e3 100644
--- a/third_party/blink/web_tests/storage/websql/fts-pointer-leak-742407.html
+++ b/third_party/blink/web_tests/storage/websql/fts-pointer-leak-742407.html
@@ -8,7 +8,8 @@
 
 async_test(testCase => {
   const database = openDatabase(
-      'Fts3CrashTest', '1.0', 'Database for FTS3 crash test', 1024 * 1024);
+      'Fts3PointerLeakTest', '1.0', 'Database for FTS3 pointer leak test',
+      1024 * 1024);
   assert_not_equals(database, null, 'openDatabase should not fail');
 
   database.transaction(testCase.step_func(transaction => {
diff --git a/third_party/blink/web_tests/transitions/transition-3d-transform-will-change-contents-expected.html b/third_party/blink/web_tests/transitions/transition-3d-transform-will-change-contents-expected.html
new file mode 100644
index 0000000..0fbfa749
--- /dev/null
+++ b/third_party/blink/web_tests/transitions/transition-3d-transform-will-change-contents-expected.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<style>
+#target {
+  width: 400px;
+  height: 100px;
+  background: blue;
+  position: relative;
+  left: 20px;
+}
+</style>
+<div id="target"></div>
diff --git a/third_party/blink/web_tests/transitions/transition-3d-transform-will-change-contents.html b/third_party/blink/web_tests/transitions/transition-3d-transform-will-change-contents.html
new file mode 100644
index 0000000..bb168e9
--- /dev/null
+++ b/third_party/blink/web_tests/transitions/transition-3d-transform-will-change-contents.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<style>
+#target {
+  width:100px;
+  height: 100px;
+  background: blue;
+  transition-duration: 0.1s;
+  transform: translate3d(10px, 0, 0);
+  will-change: contents;
+}
+#target.changed {
+  width: 400px;
+  transform: translate3d(20px, 0, 0);
+}
+</style>
+<div id="target"></div>
+<script src="../resources/run-after-layout-and-paint.js"></script>
+<script>
+if (window.testRunner)
+  testRunner.waitUntilDone();
+runAfterLayoutAndPaint(() => {
+  target.addEventListener('transitionend', () => {
+    if (window.testRunner)
+     testRunner.notifyDone();
+  });
+  target.classList.add('changed');
+});
+</script>
diff --git a/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/transitions/README.txt b/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/transitions/README.txt
new file mode 100644
index 0000000..ef8bdb24
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/transitions/README.txt
@@ -0,0 +1,2 @@
+# This suite runs tests with --disable-blink-features=BlinkGenPropertyTrees
+# and --enable-threaded-compositing.
diff --git a/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/transitions/opacity-transform-transitions-inside-iframe-expected.png b/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/transitions/opacity-transform-transitions-inside-iframe-expected.png
new file mode 100644
index 0000000..0cc6e8a0
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/transitions/opacity-transform-transitions-inside-iframe-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/mouseevent_fractional/fast/events/hit-test-counts-expected.txt b/third_party/blink/web_tests/virtual/mouseevent_fractional/fast/events/hit-test-counts-expected.txt
deleted file mode 100644
index e8366d2..0000000
--- a/third_party/blink/web_tests/virtual/mouseevent_fractional/fast/events/hit-test-counts-expected.txt
+++ /dev/null
@@ -1,93 +0,0 @@
-Count how many hit tests are required for various event scenarios. Hit tests can be expensive and it's often tempting to add more. These values should only ever be changed to go down, not up.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-Event on a simple div
----------------------
-Initial: 0+0
-MouseMove: 1+0
-MouseDown: 1+2
-MouseUp: 0+1
-Wheel: 0+1
-TouchStart: 1+1
-TouchMove: 0+0
-TouchEnd: 0+0
-GestureTapDown: 0+1
-GestureShowPress: 0+1
-GestureTap: 0+3
-GestureScrollBegin: 0+1
-GestureTapCancel: 0+1
-GestureScrollUpdate: 0+0
-GestureScrollEnd: 0+0
-DoubleFingerTouch: 2+0
-
-Event entirely over one iframe nested in another
----------------------
-Initial: 0+0 0+0 0+0
-MouseMove: 1+0 1+0 1+0
-MouseDown: 1+1 1+1 1+2
-MouseUp: 0+1 0+1 0+1
-Wheel: 0+1 0+1 0+1
-TouchStart: 2+0 2+0 1+1
-TouchMove: 0+0 0+0 0+0
-TouchEnd: 0+0 0+0 0+0
-GestureTapDown: 1+0 1+0 0+1
-GestureShowPress: 1+0 1+0 0+1
-GestureTap: 1+0 1+0 0+3
-GestureScrollBegin: 0+1 0+1 0+1
-GestureTapCancel: 1+0 1+0 0+1
-GestureScrollUpdate: 0+0 0+0 0+0
-GestureScrollEnd: 0+0 0+0 0+0
-DoubleFingerTouch: 2+0 2+0 2+0
-
-Event near boundary of two iframes
----------------------
-Initial: 0+0 0+0 0+0
-MouseMove: 1+0 1+0 0+0
-MouseDown: 1+1 1+2 0+0
-MouseUp: 0+1 0+1 0+0
-Wheel: 0+1 0+1 0+0
-TouchStart: 2+0 1+1 1+0
-TouchMove: 0+0 0+0 0+0
-TouchEnd: 0+0 0+0 0+0
-GestureTapDown: 1+0 0+1 0+0
-GestureShowPress: 1+0 0+1 0+0
-GestureTap: 1+0 0+3 0+0
-GestureScrollBegin: 0+1 0+1 0+0
-GestureTapCancel: 1+0 0+1 0+0
-GestureScrollUpdate: 0+0 0+0 0+0
-GestureScrollEnd: 0+0 0+0 0+0
-DoubleFingerTouch: 2+0 2+0 1+0
-
-Event on a simple div (desktop viewport)
----------------------
-Initial: 0+0
-MouseMove: 1+0
-MouseDown: 1+2
-MouseUp: 0+1
-Wheel: 0+1
-TouchStart: 1+1
-TouchMove: 0+0
-TouchEnd: 0+0
-GestureTapDown: 0+1
-GestureShowPress: 0+1
-GestureTap: 0+3
-GestureScrollBegin: 0+1
-GestureTapCancel: 0+1
-GestureScrollUpdate: 0+0
-GestureScrollEnd: 0+0
-DoubleFingerTouch: 2+0
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-
diff --git a/third_party/blink/web_tests/virtual/user-activation-v2/fast/events/hit-test-counts-expected.txt b/third_party/blink/web_tests/virtual/user-activation-v2/fast/events/hit-test-counts-expected.txt
deleted file mode 100644
index e8366d2..0000000
--- a/third_party/blink/web_tests/virtual/user-activation-v2/fast/events/hit-test-counts-expected.txt
+++ /dev/null
@@ -1,93 +0,0 @@
-Count how many hit tests are required for various event scenarios. Hit tests can be expensive and it's often tempting to add more. These values should only ever be changed to go down, not up.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-Event on a simple div
----------------------
-Initial: 0+0
-MouseMove: 1+0
-MouseDown: 1+2
-MouseUp: 0+1
-Wheel: 0+1
-TouchStart: 1+1
-TouchMove: 0+0
-TouchEnd: 0+0
-GestureTapDown: 0+1
-GestureShowPress: 0+1
-GestureTap: 0+3
-GestureScrollBegin: 0+1
-GestureTapCancel: 0+1
-GestureScrollUpdate: 0+0
-GestureScrollEnd: 0+0
-DoubleFingerTouch: 2+0
-
-Event entirely over one iframe nested in another
----------------------
-Initial: 0+0 0+0 0+0
-MouseMove: 1+0 1+0 1+0
-MouseDown: 1+1 1+1 1+2
-MouseUp: 0+1 0+1 0+1
-Wheel: 0+1 0+1 0+1
-TouchStart: 2+0 2+0 1+1
-TouchMove: 0+0 0+0 0+0
-TouchEnd: 0+0 0+0 0+0
-GestureTapDown: 1+0 1+0 0+1
-GestureShowPress: 1+0 1+0 0+1
-GestureTap: 1+0 1+0 0+3
-GestureScrollBegin: 0+1 0+1 0+1
-GestureTapCancel: 1+0 1+0 0+1
-GestureScrollUpdate: 0+0 0+0 0+0
-GestureScrollEnd: 0+0 0+0 0+0
-DoubleFingerTouch: 2+0 2+0 2+0
-
-Event near boundary of two iframes
----------------------
-Initial: 0+0 0+0 0+0
-MouseMove: 1+0 1+0 0+0
-MouseDown: 1+1 1+2 0+0
-MouseUp: 0+1 0+1 0+0
-Wheel: 0+1 0+1 0+0
-TouchStart: 2+0 1+1 1+0
-TouchMove: 0+0 0+0 0+0
-TouchEnd: 0+0 0+0 0+0
-GestureTapDown: 1+0 0+1 0+0
-GestureShowPress: 1+0 0+1 0+0
-GestureTap: 1+0 0+3 0+0
-GestureScrollBegin: 0+1 0+1 0+0
-GestureTapCancel: 1+0 0+1 0+0
-GestureScrollUpdate: 0+0 0+0 0+0
-GestureScrollEnd: 0+0 0+0 0+0
-DoubleFingerTouch: 2+0 2+0 1+0
-
-Event on a simple div (desktop viewport)
----------------------
-Initial: 0+0
-MouseMove: 1+0
-MouseDown: 1+2
-MouseUp: 0+1
-Wheel: 0+1
-TouchStart: 1+1
-TouchMove: 0+0
-TouchEnd: 0+0
-GestureTapDown: 0+1
-GestureShowPress: 0+1
-GestureTap: 0+3
-GestureScrollBegin: 0+1
-GestureTapCancel: 0+1
-GestureScrollUpdate: 0+0
-GestureScrollEnd: 0+0
-DoubleFingerTouch: 2+0
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-
diff --git a/third_party/boringssl/ios-aarch64/crypto/fipsmodule/vpaes-armv8.S b/third_party/boringssl/ios-aarch64/crypto/fipsmodule/vpaes-armv8.S
index d579836f..0ad9bbf3 100644
--- a/third_party/boringssl/ios-aarch64/crypto/fipsmodule/vpaes-armv8.S
+++ b/third_party/boringssl/ios-aarch64/crypto/fipsmodule/vpaes-armv8.S
@@ -11,7 +11,7 @@
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
-.text
+.section	__TEXT,__const
 
 
 .align	7	// totally strategic alignment
@@ -103,6 +103,8 @@
 .align	2
 
 .align	6
+
+.text
 ##
 ##  _aes_preheat
 ##
@@ -112,7 +114,8 @@
 
 .align	4
 _vpaes_encrypt_preheat:
-	adr	x10, Lk_inv
+	adrp	x10, Lk_inv@PAGE
+	add	x10, x10, Lk_inv@PAGEOFF
 	movi	v17.16b, #0x0f
 	ld1	{v18.2d,v19.2d}, [x10],#32	// Lk_inv
 	ld1	{v20.2d,v21.2d,v22.2d,v23.2d}, [x10],#64	// Lk_ipt, Lk_sbo
@@ -140,7 +143,8 @@
 _vpaes_encrypt_core:
 	mov	x9, x2
 	ldr	w8, [x2,#240]			// pull rounds
-	adr	x11, Lk_mc_forward+16
+	adrp	x11, Lk_mc_forward@PAGE+16
+	add	x11, x11, Lk_mc_forward@PAGEOFF+16
 						// vmovdqa	.Lk_ipt(%rip),	%xmm2	# iptlo
 	ld1	{v16.2d}, [x9], #16		// vmovdqu	(%r9),	%xmm5		# round0 key
 	and	v1.16b, v7.16b, v17.16b		// vpand	%xmm9,	%xmm0,	%xmm1
@@ -226,7 +230,8 @@
 _vpaes_encrypt_2x:
 	mov	x9, x2
 	ldr	w8, [x2,#240]			// pull rounds
-	adr	x11, Lk_mc_forward+16
+	adrp	x11, Lk_mc_forward@PAGE+16
+	add	x11, x11, Lk_mc_forward@PAGEOFF+16
 						// vmovdqa	.Lk_ipt(%rip),	%xmm2	# iptlo
 	ld1	{v16.2d}, [x9], #16		// vmovdqu	(%r9),	%xmm5		# round0 key
 	and	v1.16b,  v14.16b,  v17.16b	// vpand	%xmm9,	%xmm0,	%xmm1
@@ -329,9 +334,11 @@
 
 .align	4
 _vpaes_decrypt_preheat:
-	adr	x10, Lk_inv
+	adrp	x10, Lk_inv@PAGE
+	add	x10, x10, Lk_inv@PAGEOFF
 	movi	v17.16b, #0x0f
-	adr	x11, Lk_dipt
+	adrp	x11, Lk_dipt@PAGE
+	add	x11, x11, Lk_dipt@PAGEOFF
 	ld1	{v18.2d,v19.2d}, [x10],#32	// Lk_inv
 	ld1	{v20.2d,v21.2d,v22.2d,v23.2d}, [x11],#64	// Lk_dipt, Lk_dsbo
 	ld1	{v24.2d,v25.2d,v26.2d,v27.2d}, [x11],#64	// Lk_dsb9, Lk_dsbd
@@ -353,10 +360,12 @@
 						// vmovdqa	.Lk_dipt(%rip), %xmm2	# iptlo
 	lsl	x11, x8, #4			// mov	%rax,	%r11;	shl	$4, %r11
 	eor	x11, x11, #0x30			// xor		$0x30,	%r11
-	adr	x10, Lk_sr
+	adrp	x10, Lk_sr@PAGE
+	add	x10, x10, Lk_sr@PAGEOFF
 	and	x11, x11, #0x30			// and		$0x30,	%r11
 	add	x11, x11, x10
-	adr	x10, Lk_mc_forward+48
+	adrp	x10, Lk_mc_forward@PAGE+48
+	add	x10, x10, Lk_mc_forward@PAGEOFF+48
 
 	ld1	{v16.2d}, [x9],#16		// vmovdqu	(%r9),	%xmm4		# round0 key
 	and	v1.16b, v7.16b, v17.16b		// vpand	%xmm9,	%xmm0,	%xmm1
@@ -463,10 +472,12 @@
 						// vmovdqa	.Lk_dipt(%rip), %xmm2	# iptlo
 	lsl	x11, x8, #4			// mov	%rax,	%r11;	shl	$4, %r11
 	eor	x11, x11, #0x30			// xor		$0x30,	%r11
-	adr	x10, Lk_sr
+	adrp	x10, Lk_sr@PAGE
+	add	x10, x10, Lk_sr@PAGEOFF
 	and	x11, x11, #0x30			// and		$0x30,	%r11
 	add	x11, x11, x10
-	adr	x10, Lk_mc_forward+48
+	adrp	x10, Lk_mc_forward@PAGE+48
+	add	x10, x10, Lk_mc_forward@PAGEOFF+48
 
 	ld1	{v16.2d}, [x9],#16		// vmovdqu	(%r9),	%xmm4		# round0 key
 	and	v1.16b,  v14.16b, v17.16b	// vpand	%xmm9,	%xmm0,	%xmm1
@@ -595,14 +606,18 @@
 
 .align	4
 _vpaes_key_preheat:
-	adr	x10, Lk_inv
+	adrp	x10, Lk_inv@PAGE
+	add	x10, x10, Lk_inv@PAGEOFF
 	movi	v16.16b, #0x5b			// Lk_s63
-	adr	x11, Lk_sb1
+	adrp	x11, Lk_sb1@PAGE
+	add	x11, x11, Lk_sb1@PAGEOFF
 	movi	v17.16b, #0x0f			// Lk_s0F
 	ld1	{v18.2d,v19.2d,v20.2d,v21.2d}, [x10]		// Lk_inv, Lk_ipt
-	adr	x10, Lk_dksd
+	adrp	x10, Lk_dksd@PAGE
+	add	x10, x10, Lk_dksd@PAGEOFF
 	ld1	{v22.2d,v23.2d}, [x11]		// Lk_sb1
-	adr	x11, Lk_mc_forward
+	adrp	x11, Lk_mc_forward@PAGE
+	add	x11, x11, Lk_mc_forward@PAGEOFF
 	ld1	{v24.2d,v25.2d,v26.2d,v27.2d}, [x10],#64	// Lk_dksd, Lk_dksb
 	ld1	{v28.2d,v29.2d,v30.2d,v31.2d}, [x10],#64	// Lk_dkse, Lk_dks9
 	ld1	{v8.2d}, [x10]			// Lk_rcon
@@ -625,7 +640,9 @@
 	bl	_vpaes_schedule_transform
 	mov	v7.16b, v0.16b			// vmovdqa	%xmm0,	%xmm7
 
-	adr	x10, Lk_sr			// lea	Lk_sr(%rip),%r10
+	adrp	x10, Lk_sr@PAGE		// lea	Lk_sr(%rip),%r10
+	add	x10, x10, Lk_sr@PAGEOFF
+
 	add	x8, x8, x10
 	cbnz	w3, Lschedule_am_decrypting
 
@@ -751,12 +768,15 @@
 .align	4
 Lschedule_mangle_last:
 	// schedule last round key from xmm0
-	adr	x11, Lk_deskew			// lea	Lk_deskew(%rip),%r11	# prepare to deskew
+	adrp	x11, Lk_deskew@PAGE	// lea	Lk_deskew(%rip),%r11	# prepare to deskew
+	add	x11, x11, Lk_deskew@PAGEOFF
+
 	cbnz	w3, Lschedule_mangle_last_dec
 
 	// encrypting
 	ld1	{v1.2d}, [x8]			// vmovdqa	(%r8,%r10),%xmm1
-	adr	x11, Lk_opt			// lea	Lk_opt(%rip),	%r11		# prepare to output transform
+	adrp	x11, Lk_opt@PAGE		// lea	Lk_opt(%rip),	%r11		# prepare to output transform
+	add	x11, x11, Lk_opt@PAGEOFF
 	add	x2, x2, #32			// add	$32,	%rdx
 	tbl	v0.16b, {v0.16b}, v1.16b	// vpshufb	%xmm1,	%xmm0,	%xmm0		# output permute
 
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/vpaes-armv8.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/vpaes-armv8.S
index 8db95cb6..02b16b6 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/vpaes-armv8.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/vpaes-armv8.S
@@ -12,7 +12,7 @@
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
-.text
+.section	.rodata
 
 .type	_vpaes_consts,%object
 .align	7	// totally strategic alignment
@@ -104,6 +104,8 @@
 .align	2
 .size	_vpaes_consts,.-_vpaes_consts
 .align	6
+
+.text
 ##
 ##  _aes_preheat
 ##
@@ -113,7 +115,8 @@
 .type	_vpaes_encrypt_preheat,%function
 .align	4
 _vpaes_encrypt_preheat:
-	adr	x10, .Lk_inv
+	adrp	x10, .Lk_inv
+	add	x10, x10, :lo12:.Lk_inv
 	movi	v17.16b, #0x0f
 	ld1	{v18.2d,v19.2d}, [x10],#32	// .Lk_inv
 	ld1	{v20.2d,v21.2d,v22.2d,v23.2d}, [x10],#64	// .Lk_ipt, .Lk_sbo
@@ -141,7 +144,8 @@
 _vpaes_encrypt_core:
 	mov	x9, x2
 	ldr	w8, [x2,#240]			// pull rounds
-	adr	x11, .Lk_mc_forward+16
+	adrp	x11, .Lk_mc_forward+16
+	add	x11, x11, :lo12:.Lk_mc_forward+16
 						// vmovdqa	.Lk_ipt(%rip),	%xmm2	# iptlo
 	ld1	{v16.2d}, [x9], #16		// vmovdqu	(%r9),	%xmm5		# round0 key
 	and	v1.16b, v7.16b, v17.16b		// vpand	%xmm9,	%xmm0,	%xmm1
@@ -227,7 +231,8 @@
 _vpaes_encrypt_2x:
 	mov	x9, x2
 	ldr	w8, [x2,#240]			// pull rounds
-	adr	x11, .Lk_mc_forward+16
+	adrp	x11, .Lk_mc_forward+16
+	add	x11, x11, :lo12:.Lk_mc_forward+16
 						// vmovdqa	.Lk_ipt(%rip),	%xmm2	# iptlo
 	ld1	{v16.2d}, [x9], #16		// vmovdqu	(%r9),	%xmm5		# round0 key
 	and	v1.16b,  v14.16b,  v17.16b	// vpand	%xmm9,	%xmm0,	%xmm1
@@ -330,9 +335,11 @@
 .type	_vpaes_decrypt_preheat,%function
 .align	4
 _vpaes_decrypt_preheat:
-	adr	x10, .Lk_inv
+	adrp	x10, .Lk_inv
+	add	x10, x10, :lo12:.Lk_inv
 	movi	v17.16b, #0x0f
-	adr	x11, .Lk_dipt
+	adrp	x11, .Lk_dipt
+	add	x11, x11, :lo12:.Lk_dipt
 	ld1	{v18.2d,v19.2d}, [x10],#32	// .Lk_inv
 	ld1	{v20.2d,v21.2d,v22.2d,v23.2d}, [x11],#64	// .Lk_dipt, .Lk_dsbo
 	ld1	{v24.2d,v25.2d,v26.2d,v27.2d}, [x11],#64	// .Lk_dsb9, .Lk_dsbd
@@ -354,10 +361,12 @@
 						// vmovdqa	.Lk_dipt(%rip), %xmm2	# iptlo
 	lsl	x11, x8, #4			// mov	%rax,	%r11;	shl	$4, %r11
 	eor	x11, x11, #0x30			// xor		$0x30,	%r11
-	adr	x10, .Lk_sr
+	adrp	x10, .Lk_sr
+	add	x10, x10, :lo12:.Lk_sr
 	and	x11, x11, #0x30			// and		$0x30,	%r11
 	add	x11, x11, x10
-	adr	x10, .Lk_mc_forward+48
+	adrp	x10, .Lk_mc_forward+48
+	add	x10, x10, :lo12:.Lk_mc_forward+48
 
 	ld1	{v16.2d}, [x9],#16		// vmovdqu	(%r9),	%xmm4		# round0 key
 	and	v1.16b, v7.16b, v17.16b		// vpand	%xmm9,	%xmm0,	%xmm1
@@ -464,10 +473,12 @@
 						// vmovdqa	.Lk_dipt(%rip), %xmm2	# iptlo
 	lsl	x11, x8, #4			// mov	%rax,	%r11;	shl	$4, %r11
 	eor	x11, x11, #0x30			// xor		$0x30,	%r11
-	adr	x10, .Lk_sr
+	adrp	x10, .Lk_sr
+	add	x10, x10, :lo12:.Lk_sr
 	and	x11, x11, #0x30			// and		$0x30,	%r11
 	add	x11, x11, x10
-	adr	x10, .Lk_mc_forward+48
+	adrp	x10, .Lk_mc_forward+48
+	add	x10, x10, :lo12:.Lk_mc_forward+48
 
 	ld1	{v16.2d}, [x9],#16		// vmovdqu	(%r9),	%xmm4		# round0 key
 	and	v1.16b,  v14.16b, v17.16b	// vpand	%xmm9,	%xmm0,	%xmm1
@@ -596,14 +607,18 @@
 .type	_vpaes_key_preheat,%function
 .align	4
 _vpaes_key_preheat:
-	adr	x10, .Lk_inv
+	adrp	x10, .Lk_inv
+	add	x10, x10, :lo12:.Lk_inv
 	movi	v16.16b, #0x5b			// .Lk_s63
-	adr	x11, .Lk_sb1
+	adrp	x11, .Lk_sb1
+	add	x11, x11, :lo12:.Lk_sb1
 	movi	v17.16b, #0x0f			// .Lk_s0F
 	ld1	{v18.2d,v19.2d,v20.2d,v21.2d}, [x10]		// .Lk_inv, .Lk_ipt
-	adr	x10, .Lk_dksd
+	adrp	x10, .Lk_dksd
+	add	x10, x10, :lo12:.Lk_dksd
 	ld1	{v22.2d,v23.2d}, [x11]		// .Lk_sb1
-	adr	x11, .Lk_mc_forward
+	adrp	x11, .Lk_mc_forward
+	add	x11, x11, :lo12:.Lk_mc_forward
 	ld1	{v24.2d,v25.2d,v26.2d,v27.2d}, [x10],#64	// .Lk_dksd, .Lk_dksb
 	ld1	{v28.2d,v29.2d,v30.2d,v31.2d}, [x10],#64	// .Lk_dkse, .Lk_dks9
 	ld1	{v8.2d}, [x10]			// .Lk_rcon
@@ -626,7 +641,9 @@
 	bl	_vpaes_schedule_transform
 	mov	v7.16b, v0.16b			// vmovdqa	%xmm0,	%xmm7
 
-	adr	x10, .Lk_sr			// lea	.Lk_sr(%rip),%r10
+	adrp	x10, .Lk_sr		// lea	.Lk_sr(%rip),%r10
+	add	x10, x10, :lo12:.Lk_sr
+
 	add	x8, x8, x10
 	cbnz	w3, .Lschedule_am_decrypting
 
@@ -752,12 +769,15 @@
 .align	4
 .Lschedule_mangle_last:
 	// schedule last round key from xmm0
-	adr	x11, .Lk_deskew			// lea	.Lk_deskew(%rip),%r11	# prepare to deskew
+	adrp	x11, .Lk_deskew	// lea	.Lk_deskew(%rip),%r11	# prepare to deskew
+	add	x11, x11, :lo12:.Lk_deskew
+
 	cbnz	w3, .Lschedule_mangle_last_dec
 
 	// encrypting
 	ld1	{v1.2d}, [x8]			// vmovdqa	(%r8,%r10),%xmm1
-	adr	x11, .Lk_opt			// lea	.Lk_opt(%rip),	%r11		# prepare to output transform
+	adrp	x11, .Lk_opt		// lea	.Lk_opt(%rip),	%r11		# prepare to output transform
+	add	x11, x11, :lo12:.Lk_opt
 	add	x2, x2, #32			// add	$32,	%rdx
 	tbl	v0.16b, {v0.16b}, v1.16b	// vpshufb	%xmm1,	%xmm0,	%xmm0		# output permute
 
diff --git a/third_party/checkstyle/checkstyle-8.0-all.jar.sha1 b/third_party/checkstyle/checkstyle-8.0-all.jar.sha1
deleted file mode 100644
index 67814dc5..0000000
--- a/third_party/checkstyle/checkstyle-8.0-all.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-abf05927e50dd5ef2f5afd08716eb98498d26c4b
\ No newline at end of file
diff --git a/third_party/checkstyle/cipd.yaml b/third_party/checkstyle/cipd.yaml
new file mode 100644
index 0000000..555b69a
--- /dev/null
+++ b/third_party/checkstyle/cipd.yaml
@@ -0,0 +1,9 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# To create CIPD package run the following command.
+# cipd create --pkg-def cipd.yaml
+package: chromium/third_party/checkstyle
+data:
+  - file: checkstyle-8.0-all.jar
diff --git a/tools/chrome_proxy/webdriver/common.py b/tools/chrome_proxy/webdriver/common.py
index 59be489..63c98c5 100644
--- a/tools/chrome_proxy/webdriver/common.py
+++ b/tools/chrome_proxy/webdriver/common.py
@@ -792,6 +792,48 @@
         "found in response headers! Not expected: %s, Actual: %s" %
         (expected_via_header, actual_via_headers))
 
+  def determinePreviewShownViaHistogram(self, test_driver, preview_type):
+    """Determines if the preview type was shown by histogram check.
+
+    Checks both InfoBar and Android Omnibox histograms for the given type.
+
+    Args:
+      test_driver: The TestDriver instance.
+      preview_type: The preview type to check for.
+
+    Returns:
+      Whether a preview UI was displayed.
+    """
+    infobar_histogram_name = 'Previews.InfoBarAction.%s' % preview_type
+    android_histogram_name = 'Previews.OmniboxAction.%s' % preview_type
+
+    infobar_histogram = test_driver.GetHistogram(infobar_histogram_name, 5)
+    android_histogram = test_driver.GetHistogram(android_histogram_name, 5)
+
+    count = (infobar_histogram.get('count', 0)
+             + android_histogram.get('count', 0))
+    return count > 0
+
+  def assertPreviewShownViaHistogram(self, test_driver, preview_type):
+    """Asserts that |determinePreviewShownViaHistogram| returns true.
+
+    Args:
+      test_driver: The TestDriver instance.
+      preview_type: The preview type to check for.
+    """
+    self.assertTrue(
+      self.determinePreviewShownViaHistogram(test_driver, preview_type))
+
+  def assertPreviewNotShownViaHistogram(self, test_driver, preview_type):
+    """Asserts that |determinePreviewShownViaHistogram| returns false.
+
+    Args:
+      test_driver: The TestDriver instance.
+      preview_type: The preview type to check for.
+    """
+    self.assertFalse(
+      self.determinePreviewShownViaHistogram(test_driver, preview_type))
+
   def checkLoFiResponse(self, http_response, expected_lo_fi):
     """Asserts that if expected the response headers contain the Lo-Fi directive
     then the request headers do too. If the CPAT header contains if-heavy, the
diff --git a/tools/chrome_proxy/webdriver/https_previews.py b/tools/chrome_proxy/webdriver/https_previews.py
index 2b9abe1..b48d642e0 100644
--- a/tools/chrome_proxy/webdriver/https_previews.py
+++ b/tools/chrome_proxy/webdriver/https_previews.py
@@ -85,10 +85,7 @@
     bodyText = t.ExecuteJavascriptStatement('document.body.innerText')
     self.assertIn(expectedText, bodyText)
 
-    # Sum these because the new UI is not enabled by default in M72.
-    h1 = t.GetHistogram('Previews.OmniboxAction.LitePageRedirect', 5)
-    h2 = t.GetHistogram('Previews.InfoBarAction.LitePageRedirect', 5)
-    self.assertEqual(1, h1.get('count',0)+h2.get('count',0))
+    self.assertPreviewShownViaHistogram(t, 'LitePageRedirect')
 
   def _AssertShowingOriginalPage(self, t, expectedURL, expectedStatus):
     """Asserts that Chrome has not loaded a Lite Page from the litepages server.
@@ -105,6 +102,8 @@
 
     self.assertEqual(1, html_responses)
 
+    self.assertPreviewNotShownViaHistogram(t, 'LitePageRedirect')
+
   # Verifies that a Lite Page is not served when the server returns a bypass.
   @ChromeVersionEqualOrAfterM(74)
   def testServerReturnsBypass(self):
diff --git a/tools/chrome_proxy/webdriver/lite_page.py b/tools/chrome_proxy/webdriver/lite_page.py
index 324afefd..5038485 100644
--- a/tools/chrome_proxy/webdriver/lite_page.py
+++ b/tools/chrome_proxy/webdriver/lite_page.py
@@ -62,9 +62,7 @@
       # Verify that a Lite Page response for the main frame was seen.
       self.assertEqual(1, lite_page_responses)
 
-      # Verify previews info bar recorded
-      histogram = test_driver.GetHistogram('Previews.InfoBarAction.LitePage', 5)
-      self.assertEqual(1, histogram['count'])
+      self.assertPreviewShownViaHistogram(test_driver, 'LitePage')
 
   # Checks that a Lite Page is served and the force_lite_page experiment
   # directive is provided when always-on.
@@ -161,7 +159,8 @@
       # Need to force 2G speed to get lite-page response.
       test_driver.AddChromeArg('--force-effective-connection-type=2G')
       # Set exp=client_test_nano to force Nano response.
-      test_driver.AddChromeArg('--data-reduction-proxy-experiment=client_test_nano')
+      test_driver.AddChromeArg(
+          '--data-reduction-proxy-experiment=client_test_nano')
 
       # This page is long and has many media resources.
       test_driver.LoadURL('http://check.googlezip.net/metrics/index.html')
@@ -345,13 +344,12 @@
 
       self.assertTrue(lite_page_responses == 1 or page_policies_responses == 1)
 
-      # Verify a previews info bar recorded
       if (lite_page_responses == 1):
-        histogram = test_driver.GetHistogram(
-            'Previews.InfoBarAction.LitePage', 5)
+        self.assertPreviewShownViaHistogram(test_driver, 'LitePage')
+        self.assertPreviewNotShownViaHistogram(test_driver, 'LoFi')
       else:
-        histogram = test_driver.GetHistogram('Previews.InfoBarAction.LoFi', 5)
-      self.assertEqual(1, histogram['count'])
+        self.assertPreviewShownViaHistogram(test_driver, 'LoFi')
+        self.assertPreviewNotShownViaHistogram(test_driver, 'LitePage')
 
   # Checks that the server does not provide a preview (neither Lite Page nor
   # fallback to LoFi) for a fast connection.
@@ -389,11 +387,8 @@
           self.assertNotIn('chrome-proxy-accept-transform',
             response.request_headers)
 
-      # Verify no previews info bar recorded
-      histogram = test_driver.GetHistogram('Previews.InfoBarAction.LitePage', 5)
-      self.assertEqual(histogram, {})
-      histogram = test_driver.GetHistogram('Previews.InfoBarAction.LoFi', 5)
-      self.assertEqual(histogram, {})
+      self.assertPreviewNotShownViaHistogram(test_driver, 'LoFi')
+      self.assertPreviewNotShownViaHistogram(test_driver, 'LitePage')
 
   # Checks the default of whether server previews are enabled or not
   # based on whether running on Android (enabled) or not (disabled).
diff --git a/tools/chrome_proxy/webdriver/lofi.py b/tools/chrome_proxy/webdriver/lofi.py
index 050be5b1..ac85b81 100644
--- a/tools/chrome_proxy/webdriver/lofi.py
+++ b/tools/chrome_proxy/webdriver/lofi.py
@@ -38,9 +38,7 @@
       # Verify that Lo-Fi responses were seen.
       self.assertNotEqual(0, lofi_responses)
 
-      # Verify Lo-Fi previews info bar recorded
-      histogram = test_driver.GetHistogram('Previews.InfoBarAction.LoFi', 5)
-      self.assertEqual(1, histogram['count'])
+      self.assertPreviewShownViaHistogram(test_driver, 'LoFi')
 
   # Checks that LoFi images are served when LoFi slow connections are used and
   # the network quality estimator returns Slow2G.
@@ -75,9 +73,7 @@
       # Verify that Lo-Fi responses were seen.
       self.assertNotEqual(0, lofi_responses)
 
-      # Verify Lo-Fi previews info bar recorded
-      histogram = test_driver.GetHistogram('Previews.InfoBarAction.LoFi', 5)
-      self.assertEqual(1, histogram['count'])
+      self.assertPreviewShownViaHistogram(test_driver, 'LoFi')
 
   # Checks that LoFi images are served when LoFi slow connections are used and
   # the network quality estimator returns Slow2G.
@@ -110,9 +106,7 @@
       # Verify that Lo-Fi responses were seen.
       self.assertNotEqual(0, lofi_responses)
 
-      # Verify Lo-Fi previews info bar recorded
-      histogram = test_driver.GetHistogram('Previews.InfoBarAction.LoFi', 5)
-      self.assertEqual(1, histogram['count'])
+      self.assertPreviewShownViaHistogram(test_driver, 'LoFi')
 
   # Checks that LoFi images are NOT served when the network quality estimator
   # returns fast connection type.
@@ -151,9 +145,7 @@
           self.assertNotIn('chrome-proxy-accept-transform',
             response.request_headers)
 
-      # Verify no Lo-Fi previews info bar recorded
-      histogram = test_driver.GetHistogram('Previews.InfoBarAction.LoFi', 5)
-      self.assertEqual(histogram, {})
+      self.assertPreviewNotShownViaHistogram(test_driver, 'LoFi')
 
   # Checks that LoFi images are NOT served when the network quality estimator
   # returns fast connection.
@@ -190,9 +182,7 @@
           self.assertNotIn('chrome-proxy-accept-transform',
             response.request_headers)
 
-      # Verify no Lo-Fi previews info bar recorded
-      histogram = test_driver.GetHistogram('Previews.InfoBarAction.LoFi', 5)
-      self.assertEqual(histogram, {})
+      self.assertPreviewNotShownViaHistogram(test_driver, 'LoFi')
 
   # Checks that LoFi images are not served, but the if-heavy CPAT header is
   # added when LoFi slow connections are used and the network quality estimator
@@ -507,9 +497,7 @@
 
       self.assertNotEqual(0, image_response_count)
 
-      # Verify Lo-Fi previews info bar recorded.
-      histogram = test_driver.GetHistogram('Previews.InfoBarAction.LoFi', 5)
-      self.assertEqual(1, histogram['count'])
+      self.assertPreviewShownViaHistogram(test_driver, 'LoFi')
 
 if __name__ == '__main__':
   IntegrationTest.RunAllTests()
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 2d9478a..7608af7f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -31704,6 +31704,7 @@
   <int value="-1731149013" label="AndroidMessagesIntegration:enabled"/>
   <int value="-1729926412" label="enable-webusb-notifications"/>
   <int value="-1727530898" label="LookalikeUrlNavigationSuggestionsUI:enabled"/>
+  <int value="-1727173228" label="OmniboxZeroSuggestionsOnNTP:enabled"/>
   <int value="-1725507605" label="enable-web-midi"/>
   <int value="-1722208902" label="CCTModuleCustomRequestHeader:disabled"/>
   <int value="-1720653947" label="WebRtcHybridAgc:disabled"/>
@@ -32391,6 +32392,7 @@
   <int value="-660160292" label="enable-apps-show-on-first-paint"/>
   <int value="-654196854" label="PasswordsKeyboardAccessory:enabled"/>
   <int value="-650504533" label="enable-speculative-launch-service-worker"/>
+  <int value="-650213879" label="OmniboxZeroSuggestionsOnNTP:disabled"/>
   <int value="-650176557" label="OfflinePagesSvelteConcurrentLoading:enabled"/>
   <int value="-649956990" label="enable-harfbuzz-rendertext"/>
   <int value="-648925189" label="ExploreSites:enabled"/>
@@ -32521,6 +32523,7 @@
   <int value="-418868128" label="enable-experimental-web-platform-features"/>
   <int value="-416660617" label="EnforceTLS13Downgrade:disabled"/>
   <int value="-415186532" label="AndroidSiteSettingsUIRefresh:enabled"/>
+  <int value="-412645531" label="AutofillTokenPrefixMatching:enabled"/>
   <int value="-410852857" label="ImprovedA2HS:disabled"/>
   <int value="-408769228" label="ArcGraphicBuffersVisualizationTool:disabled"/>
   <int value="-406850932" label="EnableEmojiContextMenu:enabled"/>
@@ -32891,6 +32894,7 @@
   <int value="203776499" label="enable-virtual-keyboard-overscroll"/>
   <int value="207907053"
       label="OmniboxUIExperimentBlueSearchLoopAndSearchQuery:enabled"/>
+  <int value="209792775" label="TabGroupsAndroid:enabled"/>
   <int value="212489101" label="AutofillAssistantChromeEntry:enabled"/>
   <int value="215328738" label="ImprovedGeoLanguageData:disabled"/>
   <int value="217455219" label="SyncStandaloneTransport:enabled"/>
@@ -33029,6 +33033,7 @@
   <int value="430959979" label="SyncStandaloneTransport:disabled"/>
   <int value="431691805" label="MediaDocumentDownloadButton:enabled"/>
   <int value="434033638" label="PwaPersistentNotification:disabled"/>
+  <int value="436682243" label="TabGroupsAndroid:disabled"/>
   <int value="446316019" label="enable-threaded-compositing"/>
   <int value="451196246" label="disable-impl-side-painting"/>
   <int value="452139294" label="VrShellExperimentalRendering:enabled"/>
@@ -33621,6 +33626,7 @@
   <int value="1393500952" label="EnableVirtualKeyboardUkm:disabled"/>
   <int value="1393722373" label="SaveEditedPDFForm:disabled"/>
   <int value="1397069250" label="NetworkService:disabled"/>
+  <int value="1399950951" label="AutofillTokenPrefixMatching:disabled"/>
   <int value="1403195370" label="ArcCupsApi:enabled"/>
   <int value="1405459667" label="enable-fast-text-autosizing"/>
   <int value="1406046556" label="enable-slimming-paint-v175"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index f528d949..b3c02e9 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -54379,6 +54379,16 @@
   </summary>
 </histogram>
 
+<histogram name="Memory.ParkableString.ParkingThreadTime.5min" units="ms"
+    expires_after="2019-07-31">
+  <owner>lizeb@chromium.org</owner>
+  <summary>
+    Total thread time used by ParkableStrings for parking over the first 5
+    minutes of a renderer lifetime. Starting time is from the first
+    ParkableString being added.
+  </summary>
+</histogram>
+
 <histogram name="Memory.ParkableString.SavingsKb" units="KB">
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -145191,6 +145201,9 @@
   <suffix name="JavaHeap"
       label="Only counting memory used by Java heap in Android."/>
   <suffix name="Malloc" label="Constrained to malloc allocator."/>
+  <suffix name="Malloc.AllocatedObjects"
+      label="Only counting objects allocated using the malloc allocator. The
+             measurement is only accurate on Linux and MacOS."/>
   <suffix name="PartitionAlloc"
       label="Only counting memory used by Partition allocator."/>
   <suffix name="PartitionAlloc.AllocatedObjects"
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 7235797f..e462d43 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -3084,6 +3084,11 @@
       metrics.
     </summary>
   </metric>
+  <metric name="Malloc.AllocatedObjects">
+    <summary>
+      Measure of total memory used by objects allocated using malloc.
+    </summary>
+  </metric>
   <metric name="Net">
     <summary>
       Measure of memory allocated by network sockets and caches.
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 2441816..9d514a3 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -370,7 +370,7 @@
 crbug.com/910207 [ Nexus_5X ] v8.browsing_mobile/browse:chrome:newtab [ Skip ]
 crbug.com/853212 [ Android_Webview ] v8.browsing_mobile/browse:media:youtube [ Skip ]
 crbug.com/853212 [ Android_Webview ] v8.browsing_mobile/browse:news:cnn [ Skip ]
-crbug.com/877648 [ Android ] v8.browsing_mobile/browse:news:toi [ Skip ]
+crbug.com/877648 [ Android_Go ] v8.browsing_mobile/browse:news:toi [ Skip ]
 crbug.com/877648 [ Android_Go ] v8.browsing_mobile/browse:news:cnn [ Skip ]
 crbug.com/901967 [ Nexus6_Webview ] v8.browsing_mobile/browse:news:toi [ Skip ]
 crbug.com/923116 [ Android ] v8.browsing_mobile/browse:shopping:avito [ Skip ]
@@ -384,7 +384,7 @@
 crbug.com/910207 [ Nexus_5X ] v8.browsing_mobile-future/browse:chrome:newtab [ Skip ]
 crbug.com/799080 [ Nexus_5X Android_Webview ] v8.browsing_mobile-future/browse:social:facebook [ Skip ]
 crbug.com/799080 [ Nexus_5X Android_Webview ] v8.browsing_mobile-future/browse:social:facebook [ Skip ]
-crbug.com/901534 [ Android ] v8.browsing_mobile-future/browse:news:toi [ Skip ]
+crbug.com/901534 [ Nexus6_Webview ] v8.browsing_mobile-future/browse:news:toi [ Skip ]
 crbug.com/865400 [ Pixel_2 ] v8.browsing_mobile-future/browse:shopping:avito [ Skip ]
 crbug.com/923116 [ Android ] v8.browsing_mobile-future/browse:shopping:avito [ Skip ]
 crbug.com/906654 [ All ] v8.browsing_mobile-future/browse:tech:discourse_infinite_scroll [ Skip ]
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 1c60f05..9707fbd 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -141,7 +141,7 @@
  <item id="interest_feed_send" hash_code="76717919" type="0" content_hash_code="34678180" os_list="linux,windows" file_path="components/feed/core/feed_networking_host.cc"/>
  <item id="intranet_redirect_detector" hash_code="21785164" type="0" content_hash_code="62025595" os_list="linux,windows" file_path="chrome/browser/intranet_redirect_detector.cc"/>
  <item id="invalidation_service" hash_code="72354423" type="0" content_hash_code="78425687" os_list="linux,windows" file_path="components/invalidation/impl/gcm_network_channel.cc"/>
- <item id="kids_management_url_checker" hash_code="57474321" type="0" content_hash_code="14764704" os_list="linux,windows" file_path="chrome/browser/supervised_user/kids_management_url_checker_client.cc"/>
+ <item id="kids_management_url_checker" hash_code="57474321" type="0" content_hash_code="109271547" os_list="linux,windows" file_path="chrome/browser/supervised_user/kids_management_url_checker_client.cc"/>
  <item id="lib_address_input" hash_code="50816767" type="0" content_hash_code="57977576" os_list="linux,windows" file_path="third_party/libaddressinput/chromium/chrome_metadata_source.cc"/>
  <item id="logo_service" hash_code="35473769" type="0" content_hash_code="20271299" os_list="linux,windows" file_path="components/search_provider_logos/logo_service_impl.cc"/>
  <item id="logo_tracker" hash_code="36859107" type="0" deprecated="2018-12-07" content_hash_code="67588075" file_path=""/>
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index c58fb44..3b6311dc 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -45,23 +45,47 @@
 //
 STDMETHODIMP AXPlatformNodeTextRangeProviderWin::Clone(
     ITextRangeProvider** clone) {
+  UIA_VALIDATE_TEXTRANGEPROVIDER_CALL();
   *clone = nullptr;
 
   return CreateTextRangeProvider(owner_, start_->Clone(), end_->Clone(), clone);
 }
 
 STDMETHODIMP AXPlatformNodeTextRangeProviderWin::Compare(
-    __in ITextRangeProvider* other,
-    __out BOOL* result) {
+    ITextRangeProvider* other,
+    BOOL* result) {
   return E_NOTIMPL;
 }
 
 STDMETHODIMP AXPlatformNodeTextRangeProviderWin::CompareEndpoints(
-    TextPatternRangeEndpoint endpoint,
-    __in ITextRangeProvider* other,
-    TextPatternRangeEndpoint targetEndpoint,
-    __out int* result) {
-  return E_NOTIMPL;
+    TextPatternRangeEndpoint this_endpoint,
+    ITextRangeProvider* other,
+    TextPatternRangeEndpoint other_endpoint,
+    int* result) {
+  UIA_VALIDATE_TEXTRANGEPROVIDER_CALL();
+
+  CComPtr<AXPlatformNodeTextRangeProviderWin> other_provider;
+  if (other->QueryInterface(&other_provider) != S_OK) {
+    return UIA_E_INVALIDOPERATION;
+  }
+
+  const AXPositionInstance& this_provider_endpoint =
+      (this_endpoint == TextPatternRangeEndpoint_Start) ? start_ : end_;
+
+  const AXPositionInstance& other_provider_endpoint =
+      (other_endpoint == TextPatternRangeEndpoint_Start)
+          ? other_provider->start_
+          : other_provider->end_;
+
+  if (*this_provider_endpoint < *other_provider_endpoint) {
+    *result = -1;
+  } else if (*this_provider_endpoint > *other_provider_endpoint) {
+    *result = 1;
+  } else {
+    *result = 0;
+  }
+
+  return S_OK;
 }
 
 STDMETHODIMP AXPlatformNodeTextRangeProviderWin::ExpandToEnclosingUnit(
@@ -86,7 +110,7 @@
 }
 
 STDMETHODIMP AXPlatformNodeTextRangeProviderWin::GetAttributeValue(
-    TEXTATTRIBUTEID attributeId,
+    TEXTATTRIBUTEID attribute_id,
     VARIANT* value) {
   return E_NOTIMPL;
 }
@@ -190,9 +214,9 @@
 }
 
 STDMETHODIMP AXPlatformNodeTextRangeProviderWin::MoveEndpointByRange(
-    TextPatternRangeEndpoint endpoint,
+    TextPatternRangeEndpoint this_endpoint,
     ITextRangeProvider* other,
-    TextPatternRangeEndpoint targetEndpoint) {
+    TextPatternRangeEndpoint other_endpoint) {
   return E_NOTIMPL;
 }
 
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
index 95bd47d..aa211d6 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
@@ -42,13 +42,13 @@
   STDMETHODIMP Clone(ITextRangeProvider** clone) override;
   STDMETHODIMP Compare(ITextRangeProvider* other, BOOL* result) override;
   STDMETHODIMP
-  CompareEndpoints(TextPatternRangeEndpoint endpoint,
+  CompareEndpoints(TextPatternRangeEndpoint this_endpoint,
                    ITextRangeProvider* other,
-                   TextPatternRangeEndpoint targetEndpoint,
+                   TextPatternRangeEndpoint other_endpoint,
                    int* result) override;
   STDMETHODIMP ExpandToEnclosingUnit(TextUnit unit) override;
   STDMETHODIMP
-  FindAttribute(TEXTATTRIBUTEID attributeId,
+  FindAttribute(TEXTATTRIBUTEID attribute_id,
                 VARIANT val,
                 BOOL backward,
                 ITextRangeProvider** result) override;
@@ -57,7 +57,7 @@
            BOOL backwards,
            BOOL ignore_case,
            ITextRangeProvider** result) override;
-  STDMETHODIMP GetAttributeValue(TEXTATTRIBUTEID attributeId,
+  STDMETHODIMP GetAttributeValue(TEXTATTRIBUTEID attribute_id,
                                  VARIANT* value) override;
   STDMETHODIMP
   GetBoundingRectangles(SAFEARRAY** rectangles) override;
@@ -71,9 +71,9 @@
                      int count,
                      int* units_moved) override;
   STDMETHODIMP
-  MoveEndpointByRange(TextPatternRangeEndpoint endpoint,
+  MoveEndpointByRange(TextPatternRangeEndpoint this_endpoint,
                       ITextRangeProvider* other,
-                      TextPatternRangeEndpoint targetEndpoint) override;
+                      TextPatternRangeEndpoint other_endpoint) override;
   STDMETHODIMP Select() override;
   STDMETHODIMP AddToSelection() override;
   STDMETHODIMP RemoveFromSelection() override;
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index 9adccdf..ded7a46 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -17,6 +17,25 @@
 
 namespace ui {
 
+// Helper macros for UIAutomation HRESULT expectations
+#define EXPECT_UIA_ELEMENTNOTAVAILABLE(expr) \
+  EXPECT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE), (expr))
+#define EXPECT_UIA_INVALIDOPERATION(expr) \
+  EXPECT_EQ(static_cast<HRESULT>(UIA_E_INVALIDOPERATION), (expr))
+#define EXPECT_UIA_ELEMENTNOTENABLED(expr) \
+  EXPECT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTENABLED), (expr))
+#define EXPECT_UIA_NOTSUPPORTED(expr) \
+  EXPECT_EQ(static_cast<HRESULT>(UIA_E_NOTSUPPORTED), (expr))
+
+#define ASSERT_UIA_ELEMENTNOTAVAILABLE(expr) \
+  ASSERT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE), (expr))
+#define ASSERT_UIA_INVALIDOPERATION(expr) \
+  ASSERT_EQ(static_cast<HRESULT>(UIA_E_INVALIDOPERATION), (expr))
+#define ASSERT_UIA_ELEMENTNOTENABLED(expr) \
+  ASSERT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTENABLED), (expr))
+#define ASSERT_UIA_NOTSUPPORTED(expr) \
+  ASSERT_EQ(static_cast<HRESULT>(UIA_E_NOTSUPPORTED), (expr))
+
 #define EXPECT_UIA_DOUBLE_SAFEARRAY_EQ(safearray, expected_property_values) \
   {                                                                         \
     EXPECT_EQ(8U, ::SafeArrayGetElemsize(rectangles));                      \
@@ -54,6 +73,109 @@
   }
 };
 
+class MockAXPlatformNodeTextRangeProviderWin
+    : public CComObjectRootEx<CComMultiThreadModel>,
+      public ITextRangeProvider {
+ public:
+  BEGIN_COM_MAP(MockAXPlatformNodeTextRangeProviderWin)
+  COM_INTERFACE_ENTRY(ITextRangeProvider)
+  END_COM_MAP()
+
+  MockAXPlatformNodeTextRangeProviderWin() {}
+  ~MockAXPlatformNodeTextRangeProviderWin() {}
+
+  static HRESULT CreateMockTextRangeProvider(ITextRangeProvider** provider) {
+    CComObject<MockAXPlatformNodeTextRangeProviderWin>* text_range_provider =
+        nullptr;
+    HRESULT hr =
+        CComObject<MockAXPlatformNodeTextRangeProviderWin>::CreateInstance(
+            &text_range_provider);
+    if (SUCCEEDED(hr)) {
+      *provider = text_range_provider;
+    }
+
+    return hr;
+  }
+
+  //
+  // ITextRangeProvider methods.
+  //
+  STDMETHODIMP Clone(ITextRangeProvider** clone) override { return E_NOTIMPL; }
+
+  STDMETHODIMP Compare(ITextRangeProvider* other, BOOL* result) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHODIMP CompareEndpoints(TextPatternRangeEndpoint this_endpoint,
+                                ITextRangeProvider* other,
+                                TextPatternRangeEndpoint other_endpoint,
+                                int* result) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHODIMP ExpandToEnclosingUnit(TextUnit unit) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHODIMP FindAttribute(TEXTATTRIBUTEID attribute_id,
+                             VARIANT val,
+                             BOOL backward,
+                             ITextRangeProvider** result) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHODIMP FindText(BSTR string,
+                        BOOL backwards,
+                        BOOL ignore_case,
+                        ITextRangeProvider** result) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHODIMP GetAttributeValue(TEXTATTRIBUTEID attribute_id,
+                                 VARIANT* value) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHODIMP GetBoundingRectangles(SAFEARRAY** rectangles) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHODIMP GetEnclosingElement(
+      IRawElementProviderSimple** element) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHODIMP GetText(int max_count, BSTR* text) override { return E_NOTIMPL; }
+
+  STDMETHODIMP Move(TextUnit unit, int count, int* units_moved) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHODIMP MoveEndpointByUnit(TextPatternRangeEndpoint endpoint,
+                                  TextUnit unit,
+                                  int count,
+                                  int* units_moved) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHODIMP MoveEndpointByRange(
+      TextPatternRangeEndpoint this_endpoint,
+      ITextRangeProvider* other,
+      TextPatternRangeEndpoint other_endpoint) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHODIMP Select() override { return E_NOTIMPL; }
+
+  STDMETHODIMP AddToSelection() override { return E_NOTIMPL; }
+
+  STDMETHODIMP RemoveFromSelection() override { return E_NOTIMPL; }
+
+  STDMETHODIMP ScrollIntoView(BOOL align_to_top) override { return E_NOTIMPL; }
+
+  STDMETHODIMP GetChildren(SAFEARRAY** children) override { return E_NOTIMPL; }
+};
+
 TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderClone) {
   ui::AXNodeData root_data;
   root_data.id = 1;
@@ -118,6 +240,208 @@
   text_content.Reset();
 }
 
+TEST_F(AXPlatformNodeTextRangeProviderTest,
+       TestITextRangeProviderCompareEndpoints) {
+  ui::AXNodeData text_data;
+  text_data.id = 2;
+  text_data.role = ax::mojom::Role::kStaticText;
+  text_data.SetName("some text");
+
+  ui::AXNodeData more_text_data;
+  more_text_data.id = 3;
+  more_text_data.role = ax::mojom::Role::kStaticText;
+  more_text_data.SetName("more text");
+
+  ui::AXNodeData root_data;
+  root_data.id = 1;
+  root_data.role = ax::mojom::Role::kRootWebArea;
+  root_data.child_ids.push_back(2);
+  root_data.child_ids.push_back(3);
+
+  ui::AXTreeUpdate update;
+  ui::AXTreeData tree_data;
+  tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
+  update.tree_data = tree_data;
+  update.has_tree_data = true;
+  update.root_id = root_data.id;
+  update.nodes.push_back(root_data);
+  update.nodes.push_back(text_data);
+  update.nodes.push_back(more_text_data);
+
+  Init(update);
+
+  AXNode* root_node = GetRootNode();
+  AXNodePosition::SetTreeForTesting(tree_.get());
+  AXNode* text_node = root_node->children()[0];
+  AXNode* more_text_node = root_node->children()[1];
+
+  // Get the textRangeProvider for the document,
+  // which contains text "some textmore text".
+  ComPtr<IRawElementProviderSimple> root_node_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(root_node);
+  ComPtr<ITextProvider> document_provider;
+  EXPECT_HRESULT_SUCCEEDED(
+      root_node_raw->GetPatternProvider(UIA_TextPatternId, &document_provider));
+  ComPtr<ITextRangeProvider> document_text_range_provider;
+  EXPECT_HRESULT_SUCCEEDED(
+      document_provider->get_DocumentRange(&document_text_range_provider));
+
+  // Get the textRangeProvider for "some text".
+  ComPtr<IRawElementProviderSimple> text_node_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(text_node);
+  ComPtr<ITextProvider> text_provider;
+  EXPECT_HRESULT_SUCCEEDED(
+      text_node_raw->GetPatternProvider(UIA_TextPatternId, &text_provider));
+  ComPtr<ITextRangeProvider> text_range_provider;
+  EXPECT_HRESULT_SUCCEEDED(
+      text_provider->get_DocumentRange(&text_range_provider));
+
+  // Get the textRangeProvider for "more text".
+  ComPtr<IRawElementProviderSimple> more_text_node_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(more_text_node);
+  ComPtr<ITextProvider> more_text_provider;
+  EXPECT_HRESULT_SUCCEEDED(more_text_node_raw->GetPatternProvider(
+      UIA_TextPatternId, &more_text_provider));
+  ComPtr<ITextRangeProvider> more_text_range_provider;
+  EXPECT_HRESULT_SUCCEEDED(
+      more_text_provider->get_DocumentRange(&more_text_range_provider));
+
+  int result;
+
+  // Compare the endpoints of the document which contains "some textmore text".
+  EXPECT_HRESULT_SUCCEEDED(document_text_range_provider->CompareEndpoints(
+      TextPatternRangeEndpoint_Start, document_text_range_provider.Get(),
+      TextPatternRangeEndpoint_Start, &result));
+  EXPECT_EQ(0, result);
+
+  EXPECT_HRESULT_SUCCEEDED(document_text_range_provider->CompareEndpoints(
+      TextPatternRangeEndpoint_End, document_text_range_provider.Get(),
+      TextPatternRangeEndpoint_End, &result));
+  EXPECT_EQ(0, result);
+
+  EXPECT_HRESULT_SUCCEEDED(document_text_range_provider->CompareEndpoints(
+      TextPatternRangeEndpoint_Start, document_text_range_provider.Get(),
+      TextPatternRangeEndpoint_End, &result));
+  EXPECT_EQ(-1, result);
+
+  EXPECT_HRESULT_SUCCEEDED(document_text_range_provider->CompareEndpoints(
+      TextPatternRangeEndpoint_End, document_text_range_provider.Get(),
+      TextPatternRangeEndpoint_Start, &result));
+  EXPECT_EQ(1, result);
+
+  // Compare the endpoints of "some text" and "more text". "more text" comes
+  // after "some text", so the endpoints of "some text" precede those of
+  // "more text".
+  EXPECT_HRESULT_SUCCEEDED(text_range_provider->CompareEndpoints(
+      TextPatternRangeEndpoint_Start, more_text_range_provider.Get(),
+      TextPatternRangeEndpoint_Start, &result));
+  EXPECT_EQ(-1, result);
+
+  EXPECT_HRESULT_SUCCEEDED(text_range_provider->CompareEndpoints(
+      TextPatternRangeEndpoint_End, more_text_range_provider.Get(),
+      TextPatternRangeEndpoint_Start, &result));
+  EXPECT_EQ(-1, result);
+
+  // Compare the endpoints of "some text" with those of the entire document.
+  EXPECT_HRESULT_SUCCEEDED(text_range_provider->CompareEndpoints(
+      TextPatternRangeEndpoint_Start, document_text_range_provider.Get(),
+      TextPatternRangeEndpoint_Start, &result));
+  EXPECT_EQ(1, result);
+
+  EXPECT_HRESULT_SUCCEEDED(text_range_provider->CompareEndpoints(
+      TextPatternRangeEndpoint_End, document_text_range_provider.Get(),
+      TextPatternRangeEndpoint_End, &result));
+  EXPECT_EQ(-1, result);
+
+  // Compare the endpoints of "more text" with those of the entire document.
+  EXPECT_HRESULT_SUCCEEDED(more_text_range_provider->CompareEndpoints(
+      TextPatternRangeEndpoint_Start, document_text_range_provider.Get(),
+      TextPatternRangeEndpoint_Start, &result));
+  EXPECT_EQ(1, result);
+
+  EXPECT_HRESULT_SUCCEEDED(more_text_range_provider->CompareEndpoints(
+      TextPatternRangeEndpoint_End, document_text_range_provider.Get(),
+      TextPatternRangeEndpoint_End, &result));
+  EXPECT_EQ(0, result);
+
+  AXNodePosition::SetTreeForTesting(nullptr);
+}
+
+TEST_F(AXPlatformNodeTextRangeProviderTest,
+       TestITextRangeProviderCompareEndpointsInvalidProvider) {
+  // Test for when this provider is invalid. Because ax tree is not created,
+  // and there is no valid anchor, so this provider fails validate call.
+  {
+    ui::AXNodeData root_data;
+    root_data.id = 1;
+    root_data.role = ax::mojom::Role::kRootWebArea;
+
+    Init(root_data);
+
+    AXNode* root_node = GetRootNode();
+
+    ComPtr<IRawElementProviderSimple> root_node_raw =
+        QueryInterfaceFromNode<IRawElementProviderSimple>(root_node);
+
+    ComPtr<ITextProvider> document_provider;
+    EXPECT_HRESULT_SUCCEEDED(root_node_raw->GetPatternProvider(
+        UIA_TextPatternId, &document_provider));
+
+    ComPtr<ITextRangeProvider> text_range_provider;
+    EXPECT_HRESULT_SUCCEEDED(
+        document_provider->get_DocumentRange(&text_range_provider));
+
+    int result;
+    EXPECT_UIA_ELEMENTNOTAVAILABLE(text_range_provider->CompareEndpoints(
+        TextPatternRangeEndpoint_Start, text_range_provider.Get(),
+        TextPatternRangeEndpoint_Start, &result));
+  }
+
+  // Test for when this provider is valid, but the other provider is not an
+  // instance of AXPlatformNodeTextRangeProviderWin, so it cannot be compared
+  // to this provider.
+  {
+    ui::AXNodeData root_data;
+    root_data.id = 1;
+    root_data.role = ax::mojom::Role::kRootWebArea;
+
+    ui::AXTreeUpdate update;
+    ui::AXTreeData tree_data;
+    tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
+    update.tree_data = tree_data;
+    update.has_tree_data = true;
+    update.root_id = root_data.id;
+    update.nodes.push_back(root_data);
+
+    Init(update);
+
+    AXNode* root_node = GetRootNode();
+    AXNodePosition::SetTreeForTesting(tree_.get());
+
+    ComPtr<IRawElementProviderSimple> root_node_raw =
+        QueryInterfaceFromNode<IRawElementProviderSimple>(root_node);
+
+    ComPtr<ITextProvider> document_provider;
+    EXPECT_HRESULT_SUCCEEDED(root_node_raw->GetPatternProvider(
+        UIA_TextPatternId, &document_provider));
+
+    ComPtr<ITextRangeProvider> this_provider;
+    EXPECT_HRESULT_SUCCEEDED(
+        document_provider->get_DocumentRange(&this_provider));
+
+    int result;
+    ComPtr<ITextRangeProvider> other_provider_different_type;
+    MockAXPlatformNodeTextRangeProviderWin::CreateMockTextRangeProvider(
+        &other_provider_different_type);
+
+    EXPECT_UIA_INVALIDOPERATION(this_provider->CompareEndpoints(
+        TextPatternRangeEndpoint_Start, other_provider_different_type.Get(),
+        TextPatternRangeEndpoint_Start, &result));
+
+    AXNodePosition::SetTreeForTesting(nullptr);
+  }
+}
+
 TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderGetText) {
   ui::AXNodeData text_data;
   text_data.id = 2;
@@ -240,10 +564,8 @@
   EXPECT_HRESULT_SUCCEEDED(
       document_provider->get_DocumentRange(&text_range_provider));
 
-  ASSERT_EQ(text_range_provider->AddToSelection(),
-            static_cast<HRESULT>(UIA_E_INVALIDOPERATION));
-  ASSERT_EQ(text_range_provider->RemoveFromSelection(),
-            static_cast<HRESULT>(UIA_E_INVALIDOPERATION));
+  ASSERT_UIA_INVALIDOPERATION(text_range_provider->AddToSelection());
+  ASSERT_UIA_INVALIDOPERATION(text_range_provider->RemoveFromSelection());
 
   AXNodePosition::SetTreeForTesting(nullptr);
 }
diff --git a/ui/android/java/src/org/chromium/ui/PhotoPickerListener.java b/ui/android/java/src/org/chromium/ui/PhotoPickerListener.java
index 7dbc7d849..d6e8d90 100644
--- a/ui/android/java/src/org/chromium/ui/PhotoPickerListener.java
+++ b/ui/android/java/src/org/chromium/ui/PhotoPickerListener.java
@@ -4,6 +4,7 @@
 
 package org.chromium.ui;
 
+import android.net.Uri;
 import android.support.annotation.IntDef;
 
 import java.lang.annotation.Retention;
@@ -38,5 +39,5 @@
      *
      * @param photos The photos that were selected.
      */
-    void onPhotoPickerUserAction(@PhotoPickerAction int action, String[] photos);
+    void onPhotoPickerUserAction(@PhotoPickerAction int action, Uri[] photos);
 }
diff --git a/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java b/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java
index 1c773bb..ed3466cf 100644
--- a/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java
+++ b/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java
@@ -376,7 +376,7 @@
     }
 
     @Override
-    public void onPhotoPickerUserAction(@PhotoPickerAction int action, String[] photos) {
+    public void onPhotoPickerUserAction(@PhotoPickerAction int action, Uri[] photos) {
         switch (action) {
             case PhotoPickerAction.CANCEL:
                 onFileNotSelected();
@@ -388,21 +388,9 @@
                     return;
                 }
 
-                if (photos.length == 1) {
-                    GetDisplayNameTask task =
-                            new GetDisplayNameTask(ContextUtils.getApplicationContext(), false,
-                                    new Uri[] {Uri.parse(photos[0])});
-                    task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-                    return;
-                } else {
-                    Uri[] filePathArray = new Uri[photos.length];
-                    for (int i = 0; i < photos.length; ++i) {
-                        filePathArray[i] = Uri.parse(photos[i]);
-                    }
-                    GetDisplayNameTask task = new GetDisplayNameTask(
-                            ContextUtils.getApplicationContext(), true, filePathArray);
-                    task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-                }
+                GetDisplayNameTask task = new GetDisplayNameTask(
+                        ContextUtils.getApplicationContext(), photos.length > 1, photos);
+                task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                 break;
 
             case PhotoPickerAction.LAUNCH_GALLERY:
diff --git a/ui/aura/mus/embed_root.cc b/ui/aura/mus/embed_root.cc
index 94f7d7d8d..4ef7a75 100644
--- a/ui/aura/mus/embed_root.cc
+++ b/ui/aura/mus/embed_root.cc
@@ -6,12 +6,9 @@
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
-#include "base/scoped_observer.h"
 #include "ui/aura/client/focus_change_observer.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/mus/embed_root_delegate.h"
-#include "ui/aura/mus/focus_synchronizer.h"
-#include "ui/aura/mus/focus_synchronizer_observer.h"
 #include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/mus/window_tree_host_mus.h"
 #include "ui/aura/window.h"
@@ -23,12 +20,9 @@
 
 // FocusClient implementation used for embedded windows. This has minimal
 // checks as to what can get focus.
-class EmbeddedFocusClient : public client::FocusClient,
-                            public WindowObserver,
-                            public FocusSynchronizerObserver {
+class EmbeddedFocusClient : public client::FocusClient, public WindowObserver {
  public:
-  EmbeddedFocusClient(FocusSynchronizer* focus_synchronizer, Window* root)
-      : focus_synchronizer_(focus_synchronizer), root_(root) {
+  explicit EmbeddedFocusClient(Window* root) : root_(root) {
     client::SetFocusClient(root, this);
   }
 
@@ -48,16 +42,6 @@
   void FocusWindow(Window* window) override {
     if (IsValidWindowForFocus(window) && window != GetFocusedWindow())
       FocusWindowImpl(window);
-
-    if (GetFocusedWindow() &&
-        focus_synchronizer_->active_focus_client() != this) {
-      focus_synchronizer_->SetActiveFocusClient(this, root_);
-      scoped_focus_synchronizer_observer_.Add(focus_synchronizer_);
-    } else if (!GetFocusedWindow() &&
-               focus_synchronizer_->active_focus_client() == this) {
-      scoped_focus_synchronizer_observer_.RemoveAll();
-      focus_synchronizer_->SetActiveFocusClient(nullptr, nullptr);
-    }
   }
   void ResetFocusWithinActiveWindow(Window* window) override {
     // This is never called in the embedding case.
@@ -108,23 +92,9 @@
     DCHECK_EQ(window, focused_window_);
   }
 
-  // FocusSynchronizerObserver:
-  void OnActiveFocusClientChanged(client::FocusClient* focus_client,
-                                  Window* focus_client_root) override {
-    DCHECK_NE(this, focus_client);
-    scoped_focus_synchronizer_observer_.RemoveAll();
-    FocusWindowImpl(nullptr);
-  }
-
-  // FocusSynchronizer instance of our WindowTreeClient. Not owned.
-  FocusSynchronizer* const focus_synchronizer_;
-
   // Root of the hierarchy this is the FocusClient for.
   Window* const root_;
 
-  ScopedObserver<FocusSynchronizer, FocusSynchronizerObserver>
-      scoped_focus_synchronizer_observer_{this};
-
   Window* focused_window_ = nullptr;
 
   base::ObserverList<client::FocusChangeObserver>::Unchecked observers_;
@@ -163,17 +133,13 @@
 }
 
 void EmbedRoot::OnEmbed(std::unique_ptr<WindowTreeHostMus> window_tree_host) {
-  focus_client_ = std::make_unique<EmbeddedFocusClient>(
-      window_tree_client_->focus_synchronizer(), window_tree_host->window());
+  focus_client_ =
+      std::make_unique<EmbeddedFocusClient>(window_tree_host->window());
   window_tree_host_ = std::move(window_tree_host);
   delegate_->OnEmbed(window());
 }
 
 void EmbedRoot::OnUnembed() {
-  window_tree_host_.reset();
-  focus_client_.reset();
-
-  // |delegate_| may delete us.
   delegate_->OnUnembed();
 }
 
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index db34545..a6906cc 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -2630,27 +2630,6 @@
   window_tree_client()->OnWindowFocused(server_id(embed_root->window()));
 }
 
-// Verifies EmbedRoot window focus calls through to WindowTree and the focus is
-// cleared when the focus moves away at WS side.
-TEST_F(WindowTreeClientTest, EmbedRootWindowFocus) {
-  TestEmbedRootDelegate embed_root_delegate;
-  std::unique_ptr<EmbedRoot> embed_root =
-      window_tree_client_impl()->CreateEmbedRoot(&embed_root_delegate);
-  WindowTreeClientTestApi(window_tree_client_impl())
-      .CallOnEmbedFromToken(embed_root.get());
-  ASSERT_TRUE(embed_root->window());
-
-  embed_root->window()->Focus();
-  EXPECT_TRUE(
-      window_tree()->AckSingleChangeOfType(WindowTreeChangeType::FOCUS, true));
-  EXPECT_EQ(server_id(embed_root->window()),
-            window_tree()->last_focused_window_id());
-  EXPECT_TRUE(embed_root->window()->HasFocus());
-
-  window_tree_client_impl()->focus_synchronizer()->SetFocusFromServer(nullptr);
-  EXPECT_FALSE(embed_root->window()->HasFocus());
-}
-
 // Verifies visibility from server is applied properly when an embed root is
 // created.
 TEST_F(WindowTreeClientTest, EmbedRootVisibility) {
diff --git a/ui/aura/test/mus/test_window_tree.cc b/ui/aura/test/mus/test_window_tree.cc
index e9b4ff8..e9d8fd3 100644
--- a/ui/aura/test/mus/test_window_tree.cc
+++ b/ui/aura/test/mus/test_window_tree.cc
@@ -348,7 +348,6 @@
 void TestWindowTree::UnattachFrameSinkId(uint64_t window_id) {}
 
 void TestWindowTree::SetFocus(uint32_t change_id, ws::Id window_id) {
-  last_focused_window_id_ = window_id;
   OnChangeReceived(change_id, WindowTreeChangeType::FOCUS);
 }
 
diff --git a/ui/aura/test/mus/test_window_tree.h b/ui/aura/test/mus/test_window_tree.h
index 084bfc4..e9a7187 100644
--- a/ui/aura/test/mus/test_window_tree.h
+++ b/ui/aura/test/mus/test_window_tree.h
@@ -141,7 +141,6 @@
   ws::Id last_cancelled_window_id() const { return last_cancelled_window_id_; }
   ws::Id last_transfer_current() const { return last_transfer_current_; }
   ws::Id last_transfer_new() const { return last_transfer_new_; }
-  ws::Id last_focused_window_id() const { return last_focused_window_id_; }
   bool last_transfer_should_cancel() const {
     return last_transfer_should_cancel_;
   }
@@ -353,7 +352,6 @@
   ws::Id last_cancelled_window_id_ = 0u;
   ws::Id last_transfer_current_ = 0u;
   ws::Id last_transfer_new_ = 0u;
-  ws::Id last_focused_window_id_ = 0u;
   bool last_transfer_should_cancel_ = false;
   bool last_accepts_drops_ = false;
   size_t can_focus_count_ = 0u;
diff --git a/ui/base/now_playing/BUILD.gn b/ui/base/now_playing/BUILD.gn
index 5f17a0e1c..2870f65 100644
--- a/ui/base/now_playing/BUILD.gn
+++ b/ui/base/now_playing/BUILD.gn
@@ -4,6 +4,12 @@
 
 component("now_playing") {
   sources = [
+    "now_playing_info_center_delegate.cc",
+    "now_playing_info_center_delegate.h",
+    "now_playing_info_center_delegate_cocoa.h",
+    "now_playing_info_center_delegate_cocoa.mm",
+    "now_playing_info_center_delegate_impl.h",
+    "now_playing_info_center_delegate_impl.mm",
     "remote_command_center_delegate.cc",
     "remote_command_center_delegate.h",
     "remote_command_center_delegate_impl.h",
@@ -30,6 +36,8 @@
   testonly = true
 
   sources = [
+    "mock_now_playing_info_center_delegate.cc",
+    "mock_now_playing_info_center_delegate.h",
     "mock_remote_command_center_delegate.cc",
     "mock_remote_command_center_delegate.h",
   ]
diff --git a/ui/base/now_playing/mock_now_playing_info_center_delegate.cc b/ui/base/now_playing/mock_now_playing_info_center_delegate.cc
new file mode 100644
index 0000000..fd892e83
--- /dev/null
+++ b/ui/base/now_playing/mock_now_playing_info_center_delegate.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 "ui/base/now_playing/mock_now_playing_info_center_delegate.h"
+
+namespace now_playing {
+
+MockNowPlayingInfoCenterDelegate::MockNowPlayingInfoCenterDelegate() = default;
+
+MockNowPlayingInfoCenterDelegate::~MockNowPlayingInfoCenterDelegate() = default;
+
+}  // namespace now_playing
diff --git a/ui/base/now_playing/mock_now_playing_info_center_delegate.h b/ui/base/now_playing/mock_now_playing_info_center_delegate.h
new file mode 100644
index 0000000..7e7a7413
--- /dev/null
+++ b/ui/base/now_playing/mock_now_playing_info_center_delegate.h
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_NOW_PLAYING_MOCK_NOW_PLAYING_INFO_CENTER_DELEGATE_H_
+#define UI_BASE_NOW_PLAYING_MOCK_NOW_PLAYING_INFO_CENTER_DELEGATE_H_
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/base/now_playing/now_playing_info_center_delegate.h"
+
+namespace now_playing {
+
+// Mock implementation of NowPlayingInfoCenterDelegate for testing.
+class MockNowPlayingInfoCenterDelegate : public NowPlayingInfoCenterDelegate {
+ public:
+  MockNowPlayingInfoCenterDelegate();
+  ~MockNowPlayingInfoCenterDelegate() override;
+
+  // NowPlayingInfoCenterDelegate implementation.
+  MOCK_METHOD1(SetPlaybackState, void(PlaybackState state));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockNowPlayingInfoCenterDelegate);
+};
+
+}  // namespace now_playing
+
+#endif  // UI_BASE_NOW_PLAYING_MOCK_NOW_PLAYING_INFO_CENTER_DELEGATE_H_
diff --git a/ui/base/now_playing/now_playing_info_center_delegate.cc b/ui/base/now_playing/now_playing_info_center_delegate.cc
new file mode 100644
index 0000000..1bb1b48
--- /dev/null
+++ b/ui/base/now_playing/now_playing_info_center_delegate.cc
@@ -0,0 +1,11 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/now_playing/now_playing_info_center_delegate.h"
+
+namespace now_playing {
+
+NowPlayingInfoCenterDelegate::~NowPlayingInfoCenterDelegate() = default;
+
+}  // namespace now_playing
diff --git a/ui/base/now_playing/now_playing_info_center_delegate.h b/ui/base/now_playing/now_playing_info_center_delegate.h
new file mode 100644
index 0000000..a9158a9
--- /dev/null
+++ b/ui/base/now_playing/now_playing_info_center_delegate.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 UI_BASE_NOW_PLAYING_NOW_PLAYING_INFO_CENTER_DELEGATE_H_
+#define UI_BASE_NOW_PLAYING_NOW_PLAYING_INFO_CENTER_DELEGATE_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+
+namespace now_playing {
+
+class COMPONENT_EXPORT(NOW_PLAYING) NowPlayingInfoCenterDelegate {
+ public:
+  enum class PlaybackState {
+    kPlaying,
+    kPaused,
+    kStopped,
+  };
+
+  virtual ~NowPlayingInfoCenterDelegate();
+
+  // Creates and returns an instance of NowPlayingInfoCenterDelegate. Returns
+  // null if the current version of Mac OS does not support
+  // MPNowPlayingInfoCenter.
+  static std::unique_ptr<NowPlayingInfoCenterDelegate> Create();
+
+  // Used for setting metadata on the MPNowPlayingInfoCenter.
+  // TODO(https://crbug.com/936513): Support all metadata attributes.
+  virtual void SetPlaybackState(PlaybackState state) = 0;
+};
+
+}  // namespace now_playing
+
+#endif  // UI_BASE_NOW_PLAYING_NOW_PLAYING_INFO_CENTER_DELEGATE_H_
diff --git a/ui/base/now_playing/now_playing_info_center_delegate_cocoa.h b/ui/base/now_playing/now_playing_info_center_delegate_cocoa.h
new file mode 100644
index 0000000..096849a2
--- /dev/null
+++ b/ui/base/now_playing/now_playing_info_center_delegate_cocoa.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 UI_BASE_NOW_PLAYING_NOW_PLAYING_INFO_CENTER_DELEGATE_COCOA_H_
+#define UI_BASE_NOW_PLAYING_NOW_PLAYING_INFO_CENTER_DELEGATE_COCOA_H_
+
+#import <Cocoa/Cocoa.h>
+#import <MediaPlayer/MediaPlayer.h>
+
+API_AVAILABLE(macos(10.12.2))
+@interface NowPlayingInfoCenterDelegateCocoa : NSObject
+
+- (instancetype)init;
+
+// Clears all "Now Playing" information.
+- (void)resetNowPlayingInfo;
+
+// Called by the NowPlayingInfoCenterDelegateImpl to set metadata.
+- (void)setPlaybackState:(MPNowPlayingPlaybackState)state;
+
+@end
+
+#endif  // UI_BASE_NOW_PLAYING_NOW_PLAYING_INFO_CENTER_DELEGATE_COCOA_H_
diff --git a/ui/base/now_playing/now_playing_info_center_delegate_cocoa.mm b/ui/base/now_playing/now_playing_info_center_delegate_cocoa.mm
new file mode 100644
index 0000000..fcec2dea
--- /dev/null
+++ b/ui/base/now_playing/now_playing_info_center_delegate_cocoa.mm
@@ -0,0 +1,64 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/now_playing/now_playing_info_center_delegate_cocoa.h"
+
+#import <MediaPlayer/MediaPlayer.h>
+
+#include "base/mac/scoped_nsobject.h"
+
+@interface NowPlayingInfoCenterDelegateCocoa ()
+
+// Initialize the |nowPlayingInfo_| dictionary with values.
+- (void)initializeNowPlayingInfoValues;
+
+// Give MPNowPlayingInfoCenter the updated nowPlayingInfo_ dictionary.
+- (void)updateNowPlayingInfo;
+
+@end
+
+@implementation NowPlayingInfoCenterDelegateCocoa {
+  base::scoped_nsobject<NSMutableDictionary> nowPlayingInfo_;
+}
+
+- (instancetype)init {
+  if (self = [super init]) {
+    nowPlayingInfo_.reset([[NSMutableDictionary alloc] init]);
+    [self resetNowPlayingInfo];
+    [self updateNowPlayingInfo];
+  }
+  return self;
+}
+
+- (void)resetNowPlayingInfo {
+  [nowPlayingInfo_ removeAllObjects];
+  [self initializeNowPlayingInfoValues];
+  [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nil;
+}
+
+- (void)setPlaybackState:(MPNowPlayingPlaybackState)state {
+  [MPNowPlayingInfoCenter defaultCenter].playbackState = state;
+  [self updateNowPlayingInfo];
+}
+
+- (void)initializeNowPlayingInfoValues {
+  [nowPlayingInfo_ setObject:[NSNumber numberWithDouble:0]
+                      forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
+  [nowPlayingInfo_ setObject:[NSNumber numberWithDouble:0]
+                      forKey:MPNowPlayingInfoPropertyPlaybackRate];
+  [nowPlayingInfo_ setObject:[NSNumber numberWithDouble:0]
+                      forKey:MPMediaItemPropertyPlaybackDuration];
+#if defined(GOOGLE_CHROME_BUILD)
+  [nowPlayingInfo_ setObject:@"Chrome" forKey:MPMediaItemPropertyTitle];
+#else
+  [nowPlayingInfo_ setObject:@"Chromium" forKey:MPMediaItemPropertyTitle];
+#endif
+  [nowPlayingInfo_ setObject:@"" forKey:MPMediaItemPropertyArtist];
+}
+
+- (void)updateNowPlayingInfo {
+  [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nowPlayingInfo_;
+}
+
+@end
diff --git a/ui/base/now_playing/now_playing_info_center_delegate_impl.h b/ui/base/now_playing/now_playing_info_center_delegate_impl.h
new file mode 100644
index 0000000..c40b5aa9
--- /dev/null
+++ b/ui/base/now_playing/now_playing_info_center_delegate_impl.h
@@ -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.
+
+#ifndef UI_BASE_NOW_PLAYING_NOW_PLAYING_INFO_CENTER_DELEGATE_IMPL_H_
+#define UI_BASE_NOW_PLAYING_NOW_PLAYING_INFO_CENTER_DELEGATE_IMPL_H_
+
+#import <MediaPlayer/MediaPlayer.h>
+
+#include "base/component_export.h"
+#include "base/mac/scoped_nsobject.h"
+#include "ui/base/now_playing/now_playing_info_center_delegate.h"
+
+@class NowPlayingInfoCenterDelegateCocoa;
+
+namespace now_playing {
+
+// Implementation of NowPlayingInfoCenterDelegate that wraps an NSObject which
+// interfaces with the MPNowPlayingInfoCenter.
+class COMPONENT_EXPORT(NOW_PLAYING)
+    API_AVAILABLE(macos(10.12.2)) NowPlayingInfoCenterDelegateImpl
+    : public NowPlayingInfoCenterDelegate {
+ public:
+  NowPlayingInfoCenterDelegateImpl();
+  ~NowPlayingInfoCenterDelegateImpl() override;
+
+  // NowPlayingInfoCenterDelegate implementation.
+  void SetPlaybackState(PlaybackState state) override;
+
+ private:
+  static NowPlayingInfoCenterDelegateImpl* instance_;
+
+  MPNowPlayingPlaybackState PlaybackStateToMPNowPlayingPlaybackState(
+      PlaybackState playback_state);
+
+  base::scoped_nsobject<NowPlayingInfoCenterDelegateCocoa>
+      now_playing_info_center_delegate_cocoa_;
+
+  DISALLOW_COPY_AND_ASSIGN(NowPlayingInfoCenterDelegateImpl);
+};
+
+}  // namespace now_playing
+
+#endif  // UI_BASE_NOW_PLAYING_NOW_PLAYING_INFO_CENTER_DELEGATE_IMPL_H_
diff --git a/ui/base/now_playing/now_playing_info_center_delegate_impl.mm b/ui/base/now_playing/now_playing_info_center_delegate_impl.mm
new file mode 100644
index 0000000..3edaf250
--- /dev/null
+++ b/ui/base/now_playing/now_playing_info_center_delegate_impl.mm
@@ -0,0 +1,60 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/now_playing/now_playing_info_center_delegate_impl.h"
+
+#include "ui/base/now_playing/now_playing_info_center_delegate_cocoa.h"
+
+namespace now_playing {
+
+// static
+NowPlayingInfoCenterDelegateImpl* NowPlayingInfoCenterDelegateImpl::instance_ =
+    nullptr;
+
+// static
+std::unique_ptr<NowPlayingInfoCenterDelegate>
+NowPlayingInfoCenterDelegate::Create() {
+  if (@available(macOS 10.12.2, *))
+    return std::make_unique<NowPlayingInfoCenterDelegateImpl>();
+  return nullptr;
+}
+
+NowPlayingInfoCenterDelegateImpl::NowPlayingInfoCenterDelegateImpl() {
+  DCHECK_EQ(nullptr, instance_);
+  instance_ = this;
+  now_playing_info_center_delegate_cocoa_.reset(
+      [[NowPlayingInfoCenterDelegateCocoa alloc] init]);
+}
+
+NowPlayingInfoCenterDelegateImpl::~NowPlayingInfoCenterDelegateImpl() {
+  DCHECK_EQ(this, instance_);
+  instance_ = nullptr;
+
+  [now_playing_info_center_delegate_cocoa_ resetNowPlayingInfo];
+}
+
+void NowPlayingInfoCenterDelegateImpl::SetPlaybackState(
+    PlaybackState playback_state) {
+  MPNowPlayingPlaybackState state =
+      PlaybackStateToMPNowPlayingPlaybackState(playback_state);
+  [now_playing_info_center_delegate_cocoa_ setPlaybackState:state];
+}
+
+MPNowPlayingPlaybackState
+NowPlayingInfoCenterDelegateImpl::PlaybackStateToMPNowPlayingPlaybackState(
+    PlaybackState playback_state) {
+  switch (playback_state) {
+    case PlaybackState::kPlaying:
+      return MPNowPlayingPlaybackStatePlaying;
+    case PlaybackState::kPaused:
+      return MPNowPlayingPlaybackStatePaused;
+    case PlaybackState::kStopped:
+      return MPNowPlayingPlaybackStateStopped;
+    default:
+      NOTREACHED();
+  }
+  return MPNowPlayingPlaybackStateUnknown;
+}
+
+}  // namespace now_playing
diff --git a/ui/base/webui/jstemplate_builder.cc b/ui/base/webui/jstemplate_builder.cc
index c6615143..07ecdb9f 100644
--- a/ui/base/webui/jstemplate_builder.cc
+++ b/ui/base/webui/jstemplate_builder.cc
@@ -82,22 +82,6 @@
   output->append("</script>");
 }
 
-// Appends the source for i18n Templates in a script tag.
-void AppendI18nTemplateSourceHtml(std::string* output) {
-  base::StringPiece i18n_template_src(
-      ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
-          IDR_WEBUI_I18N_TEMPLATE_JS));
-
-  if (i18n_template_src.empty()) {
-    NOTREACHED() << "Unable to get i18n template src";
-    return;
-  }
-
-  output->append("<script>");
-  i18n_template_src.AppendToString(output);
-  output->append("</script>");
-}
-
 }  // namespace
 
 std::string GetI18nTemplateHtml(const base::StringPiece& html_template,
@@ -107,13 +91,8 @@
   std::string output =
       ui::ReplaceTemplateExpressions(html_template, replacements);
 
-  // TODO(dschuyler): After the i18n-content and i18n-values are replaced with
-  // $i18n{} replacements, we will be able to return output at this point.
-  // Remove Append*() lines that builds up the i18n replacement work to be done
-  // in JavaScript.
   AppendLoadTimeData(&output);
   AppendJsonHtml(json, &output);
-  AppendI18nTemplateSourceHtml(&output);
 
   return output;
 }
@@ -126,13 +105,8 @@
   std::string output =
       ui::ReplaceTemplateExpressions(html_template, replacements);
 
-  // TODO(dschuyler): After the i18n-content and i18n-values are replaced with
-  // $i18n{} replacements, we will be able to return output at this point.
-  // Remove Append*() lines that builds up the i18n replacement work to be done
-  // in JavaScript.
   AppendLoadTimeData(&output);
   AppendJsonHtml(json, &output);
-  AppendI18nTemplateSourceHtml(&output);
   AppendJsTemplateSourceHtml(&output);
   AppendJsTemplateProcessHtml(template_id, &output);
   return output;
diff --git a/ui/base/win/lock_state.cc b/ui/base/win/lock_state.cc
index e90a8de9..3f6d939 100644
--- a/ui/base/win/lock_state.cc
+++ b/ui/base/win/lock_state.cc
@@ -7,18 +7,18 @@
 #include <windows.h>
 #include <wtsapi32.h>
 
+#include "base/bind.h"
 #include "base/logging.h"
+#include "base/no_destructor.h"
 #include "base/win/windows_version.h"
+#include "ui/base/win/session_change_observer.h"
 
 namespace ui {
 
 namespace {
 
-// Checks if the current session is locked. Note: This API is only available
-// starting Windows 7 and up. For Windows 7 level1->SessionFlags has inverted
-// logic: https://msdn.microsoft.com/en-us/library/windows/desktop/ee621019.
+// Checks if the current session is locked.
 bool IsSessionLocked() {
-  DCHECK_GT(base::win::GetVersion(), base::win::VERSION_WIN7);
   bool is_locked = false;
   LPWSTR buffer = nullptr;
   DWORD buffer_length = 0;
@@ -26,40 +26,52 @@
                                    WTSSessionInfoEx, &buffer, &buffer_length) &&
       buffer_length >= sizeof(WTSINFOEXW)) {
     auto* info = reinterpret_cast<WTSINFOEXW*>(buffer);
-    is_locked =
-        info->Data.WTSInfoExLevel1.SessionFlags == WTS_SESSIONSTATE_LOCK;
+    auto session_flags = info->Data.WTSInfoExLevel1.SessionFlags;
+    // For Windows 7 SessionFlags has inverted logic:
+    // https://msdn.microsoft.com/en-us/library/windows/desktop/ee621019.
+    if (base::win::GetVersion() == base::win::VERSION_WIN7)
+      is_locked = session_flags == WTS_SESSIONSTATE_UNLOCK;
+    else
+      is_locked = session_flags == WTS_SESSIONSTATE_LOCK;
   }
   if (buffer)
     ::WTSFreeMemory(buffer);
   return is_locked;
 }
 
-// Checks if the current desktop is called "default" to see if we are on the
-// lock screen or on the desktop. This returns true on Windows 10 if we are on
-// the lock screen before the password prompt. Use IsSessionLocked for
-// Windows 10 and above.
-bool IsDesktopNameDefault() {
-  DCHECK_LE(base::win::GetVersion(), base::win::VERSION_WIN7);
-  bool is_locked = true;
-  HDESK input_desk = ::OpenInputDesktop(0, 0, GENERIC_READ);
-  if (input_desk) {
-    wchar_t name[256] = {0};
-    DWORD needed = 0;
-    if (::GetUserObjectInformation(
-            input_desk, UOI_NAME, name, sizeof(name), &needed)) {
-      is_locked = lstrcmpi(name, L"default") != 0;
-    }
-    ::CloseDesktop(input_desk);
+// Observes the screen lock state of Windows and caches the current state. This
+// is necessary as IsSessionLocked uses WTSQuerySessionInformation internally,
+// which is an expensive syscall and causes a performance regression as we query
+// the current state quite often. http://crbug.com/940607.
+class SessionLockedObserver {
+ public:
+  SessionLockedObserver()
+      : session_change_observer_(
+            base::BindRepeating(&SessionLockedObserver::OnSessionChange,
+                                base::Unretained(this))),
+        screen_locked_(IsSessionLocked()) {}
+
+  bool IsLocked() const { return screen_locked_; }
+
+ private:
+  void OnSessionChange(WPARAM status_code) {
+    if (status_code == WTS_SESSION_LOCK)
+      screen_locked_ = true;
+    else if (status_code == WTS_SESSION_UNLOCK)
+      screen_locked_ = false;
   }
-  return is_locked;
-}
+
+  SessionChangeObserver session_change_observer_;
+  bool screen_locked_;
+
+  DISALLOW_COPY_AND_ASSIGN(SessionLockedObserver);
+};
 
 }  // namespace
 
 bool IsWorkstationLocked() {
-  return base::win::GetVersion() <= base::win::VERSION_WIN7
-             ? IsDesktopNameDefault()
-             : IsSessionLocked();
+  static const base::NoDestructor<SessionLockedObserver> observer;
+  return observer->IsLocked();
 }
 
 }  // namespace ui
diff --git a/ui/display/manager/display_change_observer_unittest.cc b/ui/display/manager/display_change_observer_unittest.cc
index 1f2f20ae..f4ae7bb 100644
--- a/ui/display/manager/display_change_observer_unittest.cc
+++ b/ui/display/manager/display_change_observer_unittest.cc
@@ -172,8 +172,7 @@
 TEST_P(DisplayChangeObserverTest, GetEmptyExternalManagedDisplayModeList) {
   FakeDisplaySnapshot display_snapshot(
       123, gfx::Point(), gfx::Size(), DISPLAY_CONNECTION_TYPE_UNKNOWN, false,
-      false, false, false, std::string(), {}, nullptr, nullptr, 0, gfx::Size(),
-      /*has_associated_crtc=*/true);
+      false, false, false, std::string(), {}, nullptr, nullptr, 0, gfx::Size());
 
   ManagedDisplayInfo::ManagedDisplayModeList display_modes =
       DisplayChangeObserver::GetExternalManagedDisplayModeList(
diff --git a/ui/display/manager/display_configurator.cc b/ui/display/manager/display_configurator.cc
index 653057c..c81b1e8 100644
--- a/ui/display/manager/display_configurator.cc
+++ b/ui/display/manager/display_configurator.cc
@@ -1167,16 +1167,6 @@
   cached_displays_ = displays;
   has_unassociated_display_ = unassociated_displays.size();
 
-  if (has_unassociated_display_) {
-    std::string names;
-    for (const auto* unassociated_display : unassociated_displays) {
-      if (!names.empty())
-        names.push_back(',');
-      names += unassociated_display->display_name();
-    }
-    LOG(WARNING) << "Following displays have no associated crtc: " << names;
-  }
-
   if (success) {
     current_display_state_ = new_display_state;
     UpdatePowerState(new_power_state);
diff --git a/ui/display/manager/display_configurator.h b/ui/display/manager/display_configurator.h
index e81bd8d..1ceb678 100644
--- a/ui/display/manager/display_configurator.h
+++ b/ui/display/manager/display_configurator.h
@@ -484,7 +484,7 @@
 
   // Indicates whether there is any connected display having no associated crtc.
   // This can be caused by crtc shortage. When it is true, the corresponding
-  // notification will be created to inform the user.
+  // notification will be created to inform user.
   bool has_unassociated_display_;
 
   // This must be the last variable.
diff --git a/ui/display/manager/fake_display_snapshot.cc b/ui/display/manager/fake_display_snapshot.cc
index cc68b77..27df936 100644
--- a/ui/display/manager/fake_display_snapshot.cc
+++ b/ui/display/manager/fake_display_snapshot.cc
@@ -163,8 +163,7 @@
       id_, origin_, physical_size, type_, is_aspect_preserving_scaling_,
       has_overscan_, has_color_correction_matrix_,
       color_correction_in_linear_space_, name_, std::move(modes_),
-      current_mode_, native_mode_, product_code_, maximum_cursor_size_,
-      /*has_associated_crtc=*/true);
+      current_mode_, native_mode_, product_code_, maximum_cursor_size_);
 }
 
 Builder& Builder::SetId(int64_t id) {
@@ -299,8 +298,7 @@
                                          const DisplayMode* current_mode,
                                          const DisplayMode* native_mode,
                                          int64_t product_code,
-                                         const gfx::Size& maximum_cursor_size,
-                                         bool has_associated_crtc)
+                                         const gfx::Size& maximum_cursor_size)
     : DisplaySnapshot(display_id,
                       origin,
                       physical_size,
@@ -318,8 +316,7 @@
                       native_mode,
                       product_code,
                       2018 /*year_of_manufacture */,
-                      maximum_cursor_size,
-                      has_associated_crtc) {}
+                      maximum_cursor_size) {}
 
 FakeDisplaySnapshot::~FakeDisplaySnapshot() {}
 
diff --git a/ui/display/manager/fake_display_snapshot.h b/ui/display/manager/fake_display_snapshot.h
index 674279d..ec90b71 100644
--- a/ui/display/manager/fake_display_snapshot.h
+++ b/ui/display/manager/fake_display_snapshot.h
@@ -109,8 +109,7 @@
                       const DisplayMode* current_mode,
                       const DisplayMode* native_mode,
                       int64_t product_code,
-                      const gfx::Size& maximum_cursor_size,
-                      bool has_associated_crtc);
+                      const gfx::Size& maximum_cursor_size);
   ~FakeDisplaySnapshot() override;
 
   // Creates a display snapshot from the provided |spec| string. Returns null if
diff --git a/ui/display/manager/update_display_configuration_task.cc b/ui/display/manager/update_display_configuration_task.cc
index 5f8f4be..92ae15d 100644
--- a/ui/display/manager/update_display_configuration_task.cc
+++ b/ui/display/manager/update_display_configuration_task.cc
@@ -58,16 +58,7 @@
 
 void UpdateDisplayConfigurationTask::OnDisplaysUpdated(
     const std::vector<DisplaySnapshot*>& displays) {
-  cached_displays_.clear();
-  cached_unassociated_displays_.clear();
-
-  for (auto* const display : displays) {
-    if (display->has_associated_crtc())
-      cached_displays_.push_back(display);
-    else
-      cached_unassociated_displays_.push_back(display);
-  }
-
+  cached_displays_ = displays;
   requesting_displays_ = false;
 
   // If the user hasn't requested a display state, update it using the requested
diff --git a/ui/display/manager/update_display_configuration_task_unittest.cc b/ui/display/manager/update_display_configuration_task_unittest.cc
index 0dd36ac1f..1db2dcd 100644
--- a/ui/display/manager/update_display_configuration_task_unittest.cc
+++ b/ui/display/manager/update_display_configuration_task_unittest.cc
@@ -86,7 +86,6 @@
       std::vector<DisplayConfigureRequest>* requests) const override {
     gfx::Point origin;
     for (DisplaySnapshot* display : displays) {
-      DCHECK(display->has_associated_crtc());
       const DisplayMode* mode = display->native_mode();
       if (new_display_state == MULTIPLE_DISPLAY_STATE_MULTI_MIRROR)
         mode = should_mirror_ ? FindMirrorMode(displays) : nullptr;
diff --git a/ui/display/mojo/display_snapshot.mojom b/ui/display/mojo/display_snapshot.mojom
index 2ade3512..9c358b5 100644
--- a/ui/display/mojo/display_snapshot.mojom
+++ b/ui/display/mojo/display_snapshot.mojom
@@ -33,5 +33,4 @@
   int64 product_code;
   int32 year_of_manufacture;
   gfx.mojom.Size maximum_cursor_size;
-  bool has_associated_crtc;
 };
diff --git a/ui/display/mojo/display_snapshot_struct_traits.cc b/ui/display/mojo/display_snapshot_struct_traits.cc
index d4b43e71..9d97cfe 100644
--- a/ui/display/mojo/display_snapshot_struct_traits.cc
+++ b/ui/display/mojo/display_snapshot_struct_traits.cc
@@ -134,8 +134,7 @@
       data.has_color_correction_matrix(),
       data.color_correction_in_linear_space(), color_space, display_name,
       file_path, std::move(modes), std::move(edid), current_mode, native_mode,
-      data.product_code(), data.year_of_manufacture(), maximum_cursor_size,
-      data.has_associated_crtc());
+      data.product_code(), data.year_of_manufacture(), maximum_cursor_size);
   return true;
 }
 
diff --git a/ui/display/mojo/display_snapshot_struct_traits.h b/ui/display/mojo/display_snapshot_struct_traits.h
index 9ca22f0..0267d56c 100644
--- a/ui/display/mojo/display_snapshot_struct_traits.h
+++ b/ui/display/mojo/display_snapshot_struct_traits.h
@@ -113,11 +113,6 @@
     return snapshot->maximum_cursor_size();
   }
 
-  static bool has_associated_crtc(
-      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
-    return snapshot->has_associated_crtc();
-  }
-
   static bool Read(display::mojom::DisplaySnapshotDataView data,
                    std::unique_ptr<display::DisplaySnapshot>* out);
 };
diff --git a/ui/display/mojo/display_struct_traits_unittest.cc b/ui/display/mojo/display_struct_traits_unittest.cc
index 2631bba..32a460e 100644
--- a/ui/display/mojo/display_struct_traits_unittest.cc
+++ b/ui/display/mojo/display_struct_traits_unittest.cc
@@ -267,14 +267,13 @@
   const DisplayMode* current_mode = nullptr;
   const DisplayMode* native_mode = nullptr;
   const std::vector<uint8_t> edid = {1};
-  const bool has_associated_crtc = true;
 
   std::unique_ptr<DisplaySnapshot> input = std::make_unique<DisplaySnapshot>(
       display_id, origin, physical_size, type, is_aspect_preserving_scaling,
       has_overscan, has_color_correction_matrix,
       color_correction_in_linear_space, display_color_space, display_name,
       sys_path, std::move(modes), edid, current_mode, native_mode, product_code,
-      year_of_manufacture, maximum_cursor_size, has_associated_crtc);
+      year_of_manufacture, maximum_cursor_size);
 
   std::unique_ptr<DisplaySnapshot> output;
   SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
@@ -308,14 +307,13 @@
   const DisplayMode* current_mode = nullptr;
   const DisplayMode* native_mode = modes[0].get();
   const std::vector<uint8_t> edid = {1};
-  const bool has_associated_crtc = true;
 
   std::unique_ptr<DisplaySnapshot> input = std::make_unique<DisplaySnapshot>(
       display_id, origin, physical_size, type, is_aspect_preserving_scaling,
       has_overscan, has_color_correction_matrix,
       color_correction_in_linear_space, display_color_space, display_name,
       sys_path, std::move(modes), edid, current_mode, native_mode, product_code,
-      year_of_manufacture, maximum_cursor_size, has_associated_crtc);
+      year_of_manufacture, maximum_cursor_size);
 
   std::unique_ptr<DisplaySnapshot> output;
   SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
@@ -353,14 +351,13 @@
   const DisplayMode* current_mode = modes[1].get();
   const DisplayMode* native_mode = modes[2].get();
   const std::vector<uint8_t> edid = {2, 3, 4, 5};
-  const bool has_associated_crtc = true;
 
   std::unique_ptr<DisplaySnapshot> input = std::make_unique<DisplaySnapshot>(
       display_id, origin, physical_size, type, is_aspect_preserving_scaling,
       has_overscan, has_color_correction_matrix,
       color_correction_in_linear_space, display_color_space, display_name,
       sys_path, std::move(modes), edid, current_mode, native_mode, product_code,
-      year_of_manufacture, maximum_cursor_size, has_associated_crtc);
+      year_of_manufacture, maximum_cursor_size);
 
   std::unique_ptr<DisplaySnapshot> output;
   SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
@@ -394,14 +391,13 @@
   const DisplayMode* current_mode = modes[0].get();
   const DisplayMode* native_mode = modes[0].get();
   const std::vector<uint8_t> edid = {2, 3};
-  const bool has_associated_crtc = true;
 
   std::unique_ptr<DisplaySnapshot> input = std::make_unique<DisplaySnapshot>(
       display_id, origin, physical_size, type, is_aspect_preserving_scaling,
       has_overscan, has_color_correction_matrix,
       color_correction_in_linear_space, display_color_space, display_name,
       sys_path, std::move(modes), edid, current_mode, native_mode, product_code,
-      year_of_manufacture, maximum_cursor_size, has_associated_crtc);
+      year_of_manufacture, maximum_cursor_size);
 
   std::unique_ptr<DisplaySnapshot> output;
   SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
diff --git a/ui/display/types/display_snapshot.cc b/ui/display/types/display_snapshot.cc
index 0d0435b..2adf25c 100644
--- a/ui/display/types/display_snapshot.cc
+++ b/ui/display/types/display_snapshot.cc
@@ -77,8 +77,7 @@
                                  const DisplayMode* native_mode,
                                  int64_t product_code,
                                  int32_t year_of_manufacture,
-                                 const gfx::Size& maximum_cursor_size,
-                                 bool has_associated_crtc)
+                                 const gfx::Size& maximum_cursor_size)
     : display_id_(display_id),
       origin_(origin),
       physical_size_(physical_size),
@@ -96,8 +95,7 @@
       native_mode_(native_mode),
       product_code_(product_code),
       year_of_manufacture_(year_of_manufacture),
-      maximum_cursor_size_(maximum_cursor_size),
-      has_associated_crtc_(has_associated_crtc) {
+      maximum_cursor_size_(maximum_cursor_size) {
   // We must explicitly clear out the bytes that represent the serial number.
   const size_t end =
       std::min(kSerialNumberBeginingByte + kSerialNumberLengthInBytes,
@@ -129,7 +127,7 @@
       has_color_correction_matrix_, color_correction_in_linear_space_,
       color_space_, display_name_, sys_path_, std::move(clone_modes), edid_,
       cloned_current_mode, cloned_native_mode, product_code_,
-      year_of_manufacture_, maximum_cursor_size_, has_associated_crtc_);
+      year_of_manufacture_, maximum_cursor_size_);
 }
 
 std::string DisplaySnapshot::ToString() const {
diff --git a/ui/display/types/display_snapshot.h b/ui/display/types/display_snapshot.h
index ee5112b..d1dfd11 100644
--- a/ui/display/types/display_snapshot.h
+++ b/ui/display/types/display_snapshot.h
@@ -46,8 +46,7 @@
                   const DisplayMode* native_mode,
                   int64_t product_code,
                   int32_t year_of_manufacture,
-                  const gfx::Size& maximum_cursor_size,
-                  bool has_associated_crtc);
+                  const gfx::Size& maximum_cursor_size);
   virtual ~DisplaySnapshot();
 
   int64_t display_id() const { return display_id_; }
@@ -77,7 +76,6 @@
   int64_t product_code() const { return product_code_; }
   int32_t year_of_manufacture() const { return year_of_manufacture_; }
   const gfx::Size& maximum_cursor_size() const { return maximum_cursor_size_; }
-  bool has_associated_crtc() const { return has_associated_crtc_; }
 
   void add_mode(const DisplayMode* mode) { modes_.push_back(mode->Clone()); }
 
@@ -140,9 +138,6 @@
   // Maximum supported cursor size on this display.
   const gfx::Size maximum_cursor_size_;
 
-  // Indicates whether the display has an associated crtc.
-  const bool has_associated_crtc_;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(DisplaySnapshot);
 };
diff --git a/ui/file_manager/base/js/error_counter.js b/ui/file_manager/base/js/error_counter.js
index bb949dba7..b62cd10 100644
--- a/ui/file_manager/base/js/error_counter.js
+++ b/ui/file_manager/base/js/error_counter.js
@@ -100,5 +100,4 @@
     }
   };
 };
-
 })();
diff --git a/ui/file_manager/base/js/filtered_volume_manager.js b/ui/file_manager/base/js/filtered_volume_manager.js
index 7a8ae97..8d1f466 100644
--- a/ui/file_manager/base/js/filtered_volume_manager.js
+++ b/ui/file_manager/base/js/filtered_volume_manager.js
@@ -77,8 +77,7 @@
   this.volumeManager_ = null;
   this.pendingTasks_ = [];
   this.onEventBound_ = this.onEvent_.bind(this);
-  this.onVolumeInfoListUpdatedBound_ =
-      this.onVolumeInfoListUpdated_.bind(this);
+  this.onVolumeInfoListUpdatedBound_ = this.onVolumeInfoListUpdated_.bind(this);
 
   this.disposed_ = false;
 
@@ -89,8 +88,8 @@
     this.backgroundPage_ = opt_backgroundPage;
   } else {
     queue.run(callNextStep => {
-      chrome.runtime.getBackgroundPage(/** @type {function(Window=)} */(
-          opt_backgroundPage => {
+      chrome.runtime.getBackgroundPage(
+          /** @type {function(Window=)} */ (opt_backgroundPage => {
             this.backgroundPage_ = opt_backgroundPage;
             callNextStep();
           }));
@@ -98,11 +97,10 @@
   }
 
   queue.run(callNextStep => {
-    this.backgroundPage_.volumeManagerFactory.getInstance(
-        volumeManager => {
-          this.onReady_(volumeManager);
-          callNextStep();
-        });
+    this.backgroundPage_.volumeManagerFactory.getInstance(volumeManager => {
+      this.onReady_(volumeManager);
+      callNextStep();
+    });
   });
 }
 
@@ -127,8 +125,9 @@
     case AllowedPaths.ANY_PATH_OR_URL:
       return true;
     case AllowedPaths.NATIVE_OR_DRIVE_PATH:
-      return (VolumeManagerCommon.VolumeType.isNative(volumeType) ||
-              volumeType == VolumeManagerCommon.VolumeType.DRIVE);
+      return (
+          VolumeManagerCommon.VolumeType.isNative(volumeType) ||
+          volumeType == VolumeManagerCommon.VolumeType.DRIVE);
     case AllowedPaths.NATIVE_PATH:
       return VolumeManagerCommon.VolumeType.isNative(volumeType);
   }
@@ -330,16 +329,15 @@
  * @param {VolumeManagerCommon.VolumeType} volumeType Volume type.
  * @return {VolumeInfo} Found volume info.
  */
-FilteredVolumeManager.prototype.getCurrentProfileVolumeInfo =
-    function(volumeType) {
+FilteredVolumeManager.prototype.getCurrentProfileVolumeInfo = function(
+    volumeType) {
   return this.filterDisallowedVolume_(
       this.volumeManager_ &&
       this.volumeManager_.getCurrentProfileVolumeInfo(volumeType));
 };
 
 /** @override */
-FilteredVolumeManager.prototype.getDefaultDisplayRoot =
-    function(callback) {
+FilteredVolumeManager.prototype.getDefaultDisplayRoot = function(callback) {
   this.ensureInitialized(() => {
     const defaultVolume = this.getCurrentProfileVolumeInfo(
         VolumeManagerCommon.VolumeType.DOWNLOADS);
@@ -468,8 +466,7 @@
  *     the volume.
  * @private
  */
-FilteredVolumeManager.prototype.filterDisallowedVolume_ =
-    function(volumeInfo) {
+FilteredVolumeManager.prototype.filterDisallowedVolume_ = function(volumeInfo) {
   if (volumeInfo && this.isAllowedVolume_(volumeInfo)) {
     return volumeInfo;
   } else {
diff --git a/ui/file_manager/base/js/volume_manager_types.js b/ui/file_manager/base/js/volume_manager_types.js
index 15b7705c..f0d9cf8 100644
--- a/ui/file_manager/base/js/volume_manager_types.js
+++ b/ui/file_manager/base/js/volume_manager_types.js
@@ -65,11 +65,11 @@
   // Root for a drive volume.
   DRIVE: 'drive',
 
-  // The grand root entry of Team Drives in Drive volume.
-  TEAM_DRIVES_GRAND_ROOT: 'team_drives_grand_root',
+  // The grand root entry of Shared Drives in Drive volume.
+  SHARED_DRIVES_GRAND_ROOT: 'shared_drives_grand_root',
 
-  // Root directory of a Team Drive.
-  TEAM_DRIVE: 'team_drive',
+  // Root directory of a Shared Drive.
+  SHARED_DRIVE: 'team_drive',
 
   // Root for a MTP volume.
   MTP: 'mtp',
@@ -140,8 +140,8 @@
   VolumeManagerCommon.RootType.ARCHIVE,                           // 1
   VolumeManagerCommon.RootType.REMOVABLE,                         // 2
   VolumeManagerCommon.RootType.DRIVE,                             // 3
-  VolumeManagerCommon.RootType.TEAM_DRIVES_GRAND_ROOT,            // 4
-  VolumeManagerCommon.RootType.TEAM_DRIVE,                        // 5
+  VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT,          // 4
+  VolumeManagerCommon.RootType.SHARED_DRIVE,                      // 5
   VolumeManagerCommon.RootType.MTP,                               // 6
   VolumeManagerCommon.RootType.PROVIDED,                          // 7
   VolumeManagerCommon.RootType.DRIVE_OTHER,                       // 8
@@ -287,8 +287,8 @@
     case VolumeManagerCommon.RootType.REMOVABLE:
       return VolumeManagerCommon.VolumeType.REMOVABLE;
     case VolumeManagerCommon.RootType.DRIVE:
-    case VolumeManagerCommon.RootType.TEAM_DRIVES_GRAND_ROOT:
-    case VolumeManagerCommon.RootType.TEAM_DRIVE:
+    case VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT:
+    case VolumeManagerCommon.RootType.SHARED_DRIVE:
     case VolumeManagerCommon.RootType.DRIVE_OTHER:
     case VolumeManagerCommon.RootType.DRIVE_OFFLINE:
     case VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME:
diff --git a/ui/file_manager/externs/volume_info.js b/ui/file_manager/externs/volume_info.js
index 3d49d69a..bfa36cb 100644
--- a/ui/file_manager/externs/volume_info.js
+++ b/ui/file_manager/externs/volume_info.js
@@ -25,11 +25,11 @@
 VolumeInfo.prototype.displayRoot;
 
 /**
- * The display root path of Team Drives directory. It is null before finishing
+ * The display root path of Shared Drives directory. It is null before finishing
  * to resolve the entry. Valid only for Drive volume.
  * @type {DirectoryEntry}
  */
-VolumeInfo.prototype.teamDriveDisplayRoot;
+VolumeInfo.prototype.sharedDriveDisplayRoot;
 
 /**
  * The display root path of Computers directory. It is null before finishing
diff --git a/ui/file_manager/file_manager/background/js/crostini.js b/ui/file_manager/file_manager/background/js/crostini.js
index d8a6589..98dd8c9 100644
--- a/ui/file_manager/file_manager/background/js/crostini.js
+++ b/ui/file_manager/file_manager/background/js/crostini.js
@@ -47,8 +47,8 @@
   [VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT, 'DriveComputers'],
   [VolumeManagerCommon.RootType.COMPUTER, 'DriveComputers'],
   [VolumeManagerCommon.RootType.DRIVE, 'MyDrive'],
-  [VolumeManagerCommon.RootType.TEAM_DRIVES_GRAND_ROOT, 'TeamDrive'],
-  [VolumeManagerCommon.RootType.TEAM_DRIVE, 'TeamDrive'],
+  [VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT, 'TeamDrive'],
+  [VolumeManagerCommon.RootType.SHARED_DRIVE, 'TeamDrive'],
 ]);
 
 /**
diff --git a/ui/file_manager/file_manager/background/js/crostini_unittest.js b/ui/file_manager/file_manager/background/js/crostini_unittest.js
index 6e6f8ad1..acbd301c 100644
--- a/ui/file_manager/file_manager/background/js/crostini_unittest.js
+++ b/ui/file_manager/file_manager/background/js/crostini_unittest.js
@@ -137,7 +137,7 @@
   // Test with DriveFs disabled.
   setDriveFsEnabled(false);
   const disallowed = [
-    'computers_grand_root', 'computer', 'drive', 'team_drives_grand_root',
+    'computers_grand_root', 'computer', 'drive', 'shared_drives_grand_root',
     'team_drive', 'test'
   ];
   for (const type of disallowed) {
@@ -161,7 +161,7 @@
   // enforces allowed write paths.
   const allowed = [
     'downloads', 'removable', 'android_files', 'drive',
-    'team_drives_grand_root', 'team_drive'
+    'shared_drives_grand_root', 'team_drive'
   ];
   for (const type of allowed) {
     volumeManagerRootType = type;
diff --git a/ui/file_manager/file_manager/background/js/entry_location_impl.js b/ui/file_manager/file_manager/background/js/entry_location_impl.js
index dda0643f..90213cf 100644
--- a/ui/file_manager/file_manager/background/js/entry_location_impl.js
+++ b/ui/file_manager/file_manager/background/js/entry_location_impl.js
@@ -36,8 +36,8 @@
       this.rootType === VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME ||
       this.rootType === VolumeManagerCommon.RootType.DRIVE_RECENT ||
       this.rootType === VolumeManagerCommon.RootType.DRIVE_OFFLINE ||
-      this.rootType === VolumeManagerCommon.RootType.TEAM_DRIVES_GRAND_ROOT ||
-      this.rootType === VolumeManagerCommon.RootType.TEAM_DRIVE ||
+      this.rootType === VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT ||
+      this.rootType === VolumeManagerCommon.RootType.SHARED_DRIVE ||
       this.rootType === VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT ||
       this.rootType === VolumeManagerCommon.RootType.COMPUTER;
 
@@ -46,7 +46,7 @@
 
   /** @type{boolean} */
   this.hasFixedLabel = this.isRootEntry &&
-      (rootType !== VolumeManagerCommon.RootType.TEAM_DRIVE &&
+      (rootType !== VolumeManagerCommon.RootType.SHARED_DRIVE &&
        rootType !== VolumeManagerCommon.RootType.COMPUTER &&
        rootType !== VolumeManagerCommon.RootType.REMOVABLE);
 
diff --git a/ui/file_manager/file_manager/background/js/mock_volume_manager.js b/ui/file_manager/file_manager/background/js/mock_volume_manager.js
index 7b519c25..a1a3f5a 100644
--- a/ui/file_manager/file_manager/background/js/mock_volume_manager.js
+++ b/ui/file_manager/file_manager/background/js/mock_volume_manager.js
@@ -113,10 +113,10 @@
     var isRootEntry = entry.fullPath === '/root';
     if (entry.fullPath.startsWith('/team_drives')) {
       if (entry.fullPath === '/team_drives') {
-        rootType = VolumeManagerCommon.RootType.TEAM_DRIVES_GRAND_ROOT;
+        rootType = VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT;
         isRootEntry = true;
       } else {
-        rootType = VolumeManagerCommon.RootType.TEAM_DRIVE;
+        rootType = VolumeManagerCommon.RootType.SHARED_DRIVE;
         isRootEntry = util.isTeamDriveRoot(entry);
       }
     } else if (entry.fullPath.startsWith('/Computers')) {
diff --git a/ui/file_manager/file_manager/background/js/volume_info_impl.js b/ui/file_manager/file_manager/background/js/volume_info_impl.js
index 320937cf..26eccd55 100644
--- a/ui/file_manager/file_manager/background/js/volume_info_impl.js
+++ b/ui/file_manager/file_manager/background/js/volume_info_impl.js
@@ -122,11 +122,11 @@
     return this.displayRoot_;
   },
   /**
-   * @return {DirectoryEntry} The display root path of Team Drives directory.
+   * @return {DirectoryEntry} The display root path of Shared Drives directory.
    * It is null before finishing to resolve the entry. Valid only for Drive
    * volume.
    */
-  get teamDriveDisplayRoot() {
+  get sharedDriveDisplayRoot() {
     return this.sharedDriveDisplayRoot_;
   },
   /**
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_impl.js b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
index 2fd9cac5..1f9a610 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_impl.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
@@ -341,16 +341,16 @@
         entry.fullPath.indexOf(
             VolumeManagerCommon.SHARED_DRIVES_DIRECTORY_PATH + '/') === 0) {
       if (entry.fullPath == VolumeManagerCommon.SHARED_DRIVES_DIRECTORY_PATH) {
-        rootType = VolumeManagerCommon.RootType.TEAM_DRIVES_GRAND_ROOT;
+        rootType = VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT;
         isReadOnly = true;
         isRootEntry = true;
       } else {
-        rootType = VolumeManagerCommon.RootType.TEAM_DRIVE;
+        rootType = VolumeManagerCommon.RootType.SHARED_DRIVE;
         if (util.isTeamDriveRoot(entry)) {
           isReadOnly = false;
           isRootEntry = true;
         } else {
-          // Regular files/directories under Team Drives.
+          // Regular files/directories under Shared Drives.
           isRootEntry = false;
           isReadOnly = volumeInfo.isReadOnly;
         }
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_unittest.js b/ui/file_manager/file_manager/background/js/volume_manager_unittest.js
index 698367c..068396c5 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_unittest.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_unittest.js
@@ -275,7 +275,7 @@
         const teamDrivesGrandRootLocationInfo =
             volumeManager.getLocationInfo(teamDrivesGrandRoot);
         assertEquals(
-            VolumeManagerCommon.RootType.TEAM_DRIVES_GRAND_ROOT,
+            VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT,
             teamDrivesGrandRootLocationInfo.rootType);
         assertTrue(teamDrivesGrandRootLocationInfo.hasFixedLabel);
         assertTrue(teamDrivesGrandRootLocationInfo.isReadOnly);
@@ -286,7 +286,7 @@
             '/team_drives/MyTeamDrive');
         const teamDriveLocationInfo = volumeManager.getLocationInfo(teamDrive);
         assertEquals(
-            VolumeManagerCommon.RootType.TEAM_DRIVE,
+            VolumeManagerCommon.RootType.SHARED_DRIVE,
             teamDriveLocationInfo.rootType);
         assertFalse(teamDriveLocationInfo.hasFixedLabel);
         assertFalse(teamDriveLocationInfo.isReadOnly);
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index 3fb3b07..10b60c0 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -491,9 +491,9 @@
 };
 
 /**
- * Obtains whether an entry is the root directory of a Team Drive.
+ * Obtains whether an entry is the root directory of a Shared Drive.
  * @param {Entry|FilesAppEntry} entry Entry or a fake entry.
- * @return {boolean} True if the given entry is root of a Team Drive.
+ * @return {boolean} True if the given entry is root of a Shared Drive.
  */
 util.isTeamDriveRoot = entry => {
   if (entry === null) {
@@ -507,9 +507,9 @@
 };
 
 /**
- * Obtains whether an entry is the grand root directory of Team Drives.
+ * Obtains whether an entry is the grand root directory of Shared Drives.
  * @param {(!Entry|!FakeEntry)} entry Entry or a fake entry.
- * @return {boolean} True if the given entry is the grand root of Team Drives.
+ * @return {boolean} True if the given entry is the grand root of Shared Drives.
  */
 util.isTeamDrivesGrandRoot = entry => {
   if (!entry.fullPath) {
@@ -534,10 +534,10 @@
 };
 
 /**
- * Extracts Team Drive name from entry path.
+ * Extracts Shared Drive name from entry path.
  * @param {(!Entry|!FakeEntry)} entry Entry or a fake entry.
- * @return {string} The name of Team Drive. Empty string if |entry| is not
- *     under Team Drives.
+ * @return {string} The name of Shared Drive. Empty string if |entry| is not
+ *     under Shared Drives.
  */
 util.getTeamDriveName = entry => {
   if (!entry.fullPath || !util.isSharedDriveEntry(entry)) {
@@ -1064,17 +1064,17 @@
       return locationInfo.volumeInfo.label;
     case VolumeManagerCommon.RootType.DRIVE:
       return str('DRIVE_MY_DRIVE_LABEL');
-    case VolumeManagerCommon.RootType.TEAM_DRIVE:
+    case VolumeManagerCommon.RootType.SHARED_DRIVE:
     // |locationInfo| points to either the root directory of an individual Team
-    // Drive or subdirectory under it, but not the Team Drives grand directory.
-    // Every Team Drive and its subdirectories always have individual names
-    // (locationInfo.hasFixedLabel is false). So getRootTypeLabel() is only used
-    // by LocationLine.show() to display the ancestor name in the location line
-    // like this:
-    //   Team Drives > ABC Team Drive > Folder1
+    // Drive or subdirectory under it, but not the Shared Drives grand
+    // directory. Every Shared Drive and its subdirectories always have
+    // individual names (locationInfo.hasFixedLabel is false). So
+    // getRootTypeLabel() is only used by LocationLine.show() to display the
+    // ancestor name in the location line like this:
+    //   Shared Drives > ABC Shared Drive > Folder1
     //   ^^^^^^^^^^^
-    // By this reason, we return the label of the Team Drives grand root here.
-    case VolumeManagerCommon.RootType.TEAM_DRIVES_GRAND_ROOT:
+    // By this reason, we return the label of the Shared Drives grand root here.
+    case VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT:
       return str('DRIVE_SHARED_DRIVES_LABEL');
     case VolumeManagerCommon.RootType.COMPUTER:
     case VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT:
diff --git a/ui/file_manager/file_manager/foreground/css/file_types.css b/ui/file_manager/file_manager/foreground/css/file_types.css
index e54c611d..a2a3b76 100644
--- a/ui/file_manager/file_manager/foreground/css/file_types.css
+++ b/ui/file_manager/file_manager/foreground/css/file_types.css
@@ -274,13 +274,13 @@
       url(../images/volumes/2x/service_drive_active.png) 2x);
 }
 
-[volume-type-icon='team_drives_grand_root'] {
+[volume-type-icon='shared_drives_grand_root'] {
   background-image: -webkit-image-set(
       url(../images/volumes/team_drive.png) 1x,
       url(../images/volumes/2x/team_drive.png) 2x);
 }
 
-.tree-row[selected] [volume-type-icon='team_drives_grand_root'] {
+.tree-row[selected] [volume-type-icon='shared_drives_grand_root'] {
   background-image: -webkit-image-set(
       url(../images/volumes/team_drive_active.png) 1x,
       url(../images/volumes/2x/team_drive_active.png) 2x);
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index a57076b7..f2195dff 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -1855,9 +1855,9 @@
         (info.rootType == VolumeManagerCommon.RootType.DRIVE ||
          info.rootType == VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT ||
          info.rootType ==
-             VolumeManagerCommon.RootType.TEAM_DRIVES_GRAND_ROOT)) {
-      // Only show the dialog for My Drive, Team Drives Grand Root and
-      // Computers Grand Root.  Do not show for roots of a single Team Drive
+             VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT)) {
+      // Only show the dialog for My Drive, Shared Drives Grand Root and
+      // Computers Grand Root.  Do not show for roots of a single Shared Drive
       // or Computer.
       fileManager.ui_.confirmDialog.showHtml(
           strf('SHARE_ROOT_FOLDER_WITH_CROSTINI_TITLE'),
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
index f6492cc6..0d3bd11 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -271,10 +271,10 @@
  */
 FileTransferController.ConfirmationType = {
   NONE: 'none',
-  MOVE_BETWEEN_TEAM_DRIVES: 'between_team_drives',
-  MOVE_FROM_TEAM_DRIVE_TO_OTHER: 'move_from_team_drive_to_other',
-  MOVE_FROM_OTHER_TO_TEAM_DRIVE: 'move_from_other_to_team_drive',
-  COPY_FROM_OTHER_TO_TEAM_DRIVE: 'copy_from_other_to_team_drive',
+  MOVE_BETWEEN_SHARED_DRIVES: 'between_team_drives',
+  MOVE_FROM_SHARED_DRIVE_TO_OTHER: 'move_from_team_drive_to_other',
+  MOVE_FROM_OTHER_TO_SHARED_DRIVE: 'move_from_other_to_team_drive',
+  COPY_FROM_OTHER_TO_SHARED_DRIVE: 'copy_from_other_to_team_drive',
 };
 
 /**
@@ -304,27 +304,27 @@
           return FileTransferController.ConfirmationType.NONE;
         } else {
           return FileTransferController.ConfirmationType
-              .MOVE_BETWEEN_TEAM_DRIVES;
+              .MOVE_BETWEEN_SHARED_DRIVES;
         }
       } else {
         return FileTransferController.ConfirmationType
-            .MOVE_FROM_TEAM_DRIVE_TO_OTHER;
+            .MOVE_FROM_SHARED_DRIVE_TO_OTHER;
       }
     } else if (destination.isTeamDrive) {
       return FileTransferController.ConfirmationType
-          .MOVE_FROM_OTHER_TO_TEAM_DRIVE;
+          .MOVE_FROM_OTHER_TO_SHARED_DRIVE;
     }
     return FileTransferController.ConfirmationType.NONE;
   } else {
     if (!destination.isTeamDrive) {
       return FileTransferController.ConfirmationType.NONE;
     }
-    // Copying to Team Drive.
+    // Copying to Shared Drive.
     if (!(source.isTeamDrive &&
           source.teamDriveName == destination.teamDriveName)) {
-      // This is not a copy within the same Team Drive.
+      // This is not a copy within the same Shared Drive.
       return FileTransferController.ConfirmationType
-          .COPY_FROM_OTHER_TO_TEAM_DRIVE;
+          .COPY_FROM_OTHER_TO_SHARED_DRIVE;
     }
     return FileTransferController.ConfirmationType.NONE;
   }
@@ -342,22 +342,25 @@
   const sourceName = util.getTeamDriveName(sourceEntries[0]);
   const destinationName = util.getTeamDriveName(this.destinationEntry);
   switch (confirmationType) {
-    case FileTransferController.ConfirmationType.MOVE_BETWEEN_TEAM_DRIVES:
+    case FileTransferController.ConfirmationType.MOVE_BETWEEN_SHARED_DRIVES:
       return [
         strf('DRIVE_CONFIRM_TD_MEMBERS_LOSE_ACCESS', sourceName),
         strf('DRIVE_CONFIRM_TD_MEMBERS_GAIN_ACCESS_TO_COPY', destinationName)
       ];
-    // TODO(yamaguchi): notify ownership transfer if the two Team Drives
+    // TODO(yamaguchi): notify ownership transfer if the two Shared Drives
     // belong to different domains.
-    case FileTransferController.ConfirmationType.MOVE_FROM_TEAM_DRIVE_TO_OTHER:
+    case FileTransferController.ConfirmationType
+        .MOVE_FROM_SHARED_DRIVE_TO_OTHER:
       return [
         strf('DRIVE_CONFIRM_TD_MEMBERS_LOSE_ACCESS', sourceName)
         // TODO(yamaguchi): Warn if the operation moves at least one
         // directory to My Drive, as it's no undoable.
       ];
-    case FileTransferController.ConfirmationType.MOVE_FROM_OTHER_TO_TEAM_DRIVE:
+    case FileTransferController.ConfirmationType
+        .MOVE_FROM_OTHER_TO_SHARED_DRIVE:
       return [strf('DRIVE_CONFIRM_TD_MEMBERS_GAIN_ACCESS', destinationName)];
-    case FileTransferController.ConfirmationType.COPY_FROM_OTHER_TO_TEAM_DRIVE:
+    case FileTransferController.ConfirmationType
+        .COPY_FROM_OTHER_TO_SHARED_DRIVE:
       return [strf(
           'DRIVE_CONFIRM_TD_MEMBERS_GAIN_ACCESS_TO_COPY', destinationName)];
   }
@@ -735,14 +738,14 @@
             destinationLocationInfo.rootType) !==
         VolumeManagerCommon.VolumeType.DRIVE;
 
-    // Disallow transferring hosted files from Team Drives to outside of Drive.
-    // This is because hosted files aren't 'real' files, so it doesn't make
-    // sense to allow a 'local' copy (e.g. in Downloads, or on a USB), where the
-    // file can't be accessed offline (or necessarily accessed at all) by the
-    // person who tries to open it.
-    // In future, block this for all hosted files, regardless of their source.
-    // For now, to maintain backwards-compatibility, just block this for hosted
-    // files stored in a Team Drive.
+    // Disallow transferring hosted files from Shared Drives to outside of
+    // Drive. This is because hosted files aren't 'real' files, so it doesn't
+    // make sense to allow a 'local' copy (e.g. in Downloads, or on a USB),
+    // where the file can't be accessed offline (or necessarily accessed at all)
+    // by the person who tries to open it. In future, block this for all hosted
+    // files, regardless of their source. For now, to maintain
+    // backwards-compatibility, just block this for hosted files stored in a
+    // Shared Drive.
     if (sourceEntries.some(
             entry =>
                 util.isSharedDriveEntry(entry) && FileType.isHosted(entry)) &&
@@ -1439,7 +1442,7 @@
       return false;
     }
 
-    // Cut is unavailable on Team Drive roots.
+    // Cut is unavailable on Shared Drive roots.
     if (util.isTeamDriveRoot(entry)) {
       return false;
     }
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index 24969093..508d7f2 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -256,8 +256,8 @@
     return locationInfo &&
         (locationInfo.rootType === VolumeManagerCommon.RootType.DRIVE ||
          locationInfo.rootType ===
-             VolumeManagerCommon.RootType.TEAM_DRIVES_GRAND_ROOT ||
-         locationInfo.rootType === VolumeManagerCommon.RootType.TEAM_DRIVE ||
+             VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT ||
+         locationInfo.rootType === VolumeManagerCommon.RootType.SHARED_DRIVE ||
          locationInfo.rootType ===
              VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT ||
          locationInfo.rootType === VolumeManagerCommon.RootType.COMPUTER ||
@@ -270,7 +270,7 @@
 
   /**
    * If the this directory supports 'shared' feature, as in displays shared
-   * icon. It's only supported inside 'My Drive', even Team Drive doesn't
+   * icon. It's only supported inside 'My Drive', even Shared Drive doesn't
    * support it.
    * @type {!boolean}
    */
@@ -1115,7 +1115,7 @@
 
 /**
  * A TreeItem which represents a Drive volume. Drive volume has fake entries
- * such as Team Drives, Shared with me, and Offline in it.
+ * such as Shared Drives, Shared with me, and Offline in it.
  *
  * @param {!NavigationModelVolumeItem} modelItem NavigationModelItem of this
  *     volume.
@@ -1168,23 +1168,23 @@
 };
 
 /**
- * Creates Team Drives root if there is any team drive, if there is no team
+ * Creates Shared Drives root if there is any team drive, if there is no team
  * drive, then it removes the root.
  *
  * Since we don't currently support any functionality with just the grand root
  * (e.g. you can't create a new team drive from the root yet), remove/don't
  * create the grand root so it can't be reached via keyboard.
- * If there is at least one Team Drive, add/show the Team Drives grand root.
+ * If there is at least one Shared Drive, add/show the Shared Drives grand root.
  *
- * @return {!Promise<SubDirectoryItem>} Resolved with Team Drive Grand Root
+ * @return {!Promise<SubDirectoryItem>} Resolved with Shared Drive Grand Root
  * SubDirectoryItem instance, or undefined when it shouldn't exist.
  * @private
  */
-DriveVolumeItem.prototype.createTeamDrivesGrandRoot_ = function() {
+DriveVolumeItem.prototype.createSharedDrivesGrandRoot_ = function() {
   return new Promise((resolve) => {
-    const teamDriveGrandRoot = this.volumeInfo_.teamDriveDisplayRoot;
-    if (!teamDriveGrandRoot) {
-      // Team Drive is disabled.
+    const sharedDriveGrandRoot = this.volumeInfo_.sharedDriveDisplayRoot;
+    if (!sharedDriveGrandRoot) {
+      // Shared Drive is disabled.
       resolve();
       return;
     }
@@ -1192,13 +1192,13 @@
     let index;
     for (let i = 0; i < this.items.length; i++) {
       const entry = this.items[i] && this.items[i].entry;
-      if (entry && util.isSameEntry(entry, teamDriveGrandRoot)) {
+      if (entry && util.isSameEntry(entry, sharedDriveGrandRoot)) {
         index = i;
         break;
       }
     }
 
-    const reader = teamDriveGrandRoot.createReader();
+    const reader = sharedDriveGrandRoot.createReader();
     reader.readEntries((results) => {
       metrics.recordSmallCount('TeamDrivesCount', results.length);
       // Only create grand root if there is at least 1 child/result.
@@ -1212,11 +1212,11 @@
         // Create if it doesn't exist yet.
         const label = util.getEntryLabel(
                           this.parentTree_.volumeManager_.getLocationInfo(
-                              teamDriveGrandRoot),
-                          teamDriveGrandRoot) ||
+                              sharedDriveGrandRoot),
+                          sharedDriveGrandRoot) ||
             '';
         const item = new SubDirectoryItem(
-            label, teamDriveGrandRoot, this, this.parentTree_);
+            label, sharedDriveGrandRoot, this, this.parentTree_);
         this.addAt(item, 1);
         item.updateSubDirectories(false);
         resolve(item);
@@ -1283,7 +1283,7 @@
             '';
         const item = new SubDirectoryItem(
             label, computerGrandRoot, this, this.parentTree_);
-        // We want to show "Computers" after "Team Drives", the
+        // We want to show "Computers" after "Shared Drives", the
         // computersIndexPosition_() helper function will work out the correct
         // index to place "Computers" at.
         const position = this.computersIndexPosition_();
@@ -1337,7 +1337,7 @@
 
   let entries = [this.entry];
 
-  const teamDrivesDisplayRoot = this.volumeInfo_.teamDriveDisplayRoot;
+  const teamDrivesDisplayRoot = this.volumeInfo_.sharedDriveDisplayRoot;
   if (teamDrivesDisplayRoot) {
     entries.push(teamDrivesDisplayRoot);
   }
@@ -1367,7 +1367,7 @@
     // Only create the team drives root if there is at least 1 team drive.
     const entry = entries[i];
     if (entry === teamDrivesDisplayRoot) {
-      this.createTeamDrivesGrandRoot_();
+      this.createSharedDrivesGrandRoot_();
     } else if (entry === computersDisplayRoot) {
       this.createComputersGrandRoot_();
     } else {
@@ -1393,12 +1393,12 @@
 DriveVolumeItem.prototype.updateItemByEntry = function(changedDirectoryEntry) {
   const isTeamDriveChild = util.isSharedDriveEntry(changedDirectoryEntry);
 
-  // If Team Drive grand root has been removed and we receive an update for an
-  // team drive, we need to create the Team Drive grand root.
+  // If Shared Drive grand root has been removed and we receive an update for an
+  // team drive, we need to create the Shared Drive grand root.
   if (isTeamDriveChild) {
-    this.createTeamDrivesGrandRoot_().then((teamDriveGrandRootItem) => {
-      if (teamDriveGrandRootItem) {
-        teamDriveGrandRootItem.updateItemByEntry(changedDirectoryEntry);
+    this.createSharedDrivesGrandRoot_().then((sharedDriveGrandRootItem) => {
+      if (sharedDriveGrandRootItem) {
+        sharedDriveGrandRootItem.updateItemByEntry(changedDirectoryEntry);
       }
     });
     return;
@@ -1437,7 +1437,7 @@
 DriveVolumeItem.prototype.computersIndexPosition_ = function() {
   // We want the order to be
   // - My Drive
-  // - Team Drives (if the user has any)
+  // - Shared Drives (if the user has any)
   // - Computers (if the user has any)
   // So if the user has team drives we want index position 2, otherwise index
   // position 1.
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 35e62d0..da8d0a12 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
@@ -481,7 +481,7 @@
   }
 
   // Space
-  if (e.key == ' ') {
+  if (e.code == 'Space') {
     if (leadIndex != -1) {
       const selected = sm.getIndexSelected(leadIndex);
       if (e.ctrlKey) {
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table_list_unittest.js b/ui/file_manager/file_manager/foreground/js/ui/file_table_list_unittest.js
index d7302002..1af2089d 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_table_list_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_table_list_unittest.js
@@ -70,8 +70,11 @@
   };
 }
 
-/** @param {string} keyName */
-function ctrlAndKey(keyName) {
+/**
+ * @param {string} keyName
+ * @param {string=} code event.code value.
+ */
+function ctrlAndKey(keyName, code) {
   return {
     ctrlKey: true,
     shiftKey: false,
@@ -79,6 +82,7 @@
     bubbles: true,
     composed: true,
     key: keyName,
+    code: code,
     // Get keyCode for key like A, B but not for Escape, Arrow, etc.
     // A==65, B==66, etc.
     keyCode: (keyName && keyName.length === 1) ? keyName.charCodeAt(0) :
@@ -182,7 +186,8 @@
   assertFalse(sm.getCheckSelectMode());
 
   // Ctrl+Space selects the focused item.
-  tableList.dispatchEvent(new KeyboardEvent('keydown', ctrlAndKey(' ')));
+  tableList.dispatchEvent(
+      new KeyboardEvent('keydown', ctrlAndKey(' ', 'Space')));
   // Multiple selection mode should now be activated.
   assertTrue(sm.getCheckSelectMode());
   // Both listItem1 and listItem2 should be selected.
diff --git a/ui/file_manager/file_manager/foreground/js/ui/location_line.js b/ui/file_manager/file_manager/foreground/js/ui/location_line.js
index 3b2e49a65..f081ca4 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/location_line.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/location_line.js
@@ -105,7 +105,7 @@
         sharedWithMeFakeEntry.toURL(),
         sharedWithMeFakeEntry));
   } else if (
-      locationInfo.rootType === VolumeManagerCommon.RootType.TEAM_DRIVE) {
+      locationInfo.rootType === VolumeManagerCommon.RootType.SHARED_DRIVE) {
     displayRootUrl = this.replaceRootName_(
         displayRootUrl, VolumeManagerCommon.SHARED_DRIVES_DIRECTORY_PATH);
     components.push(new LocationLine.PathComponent(
diff --git a/ui/file_manager/integration_tests/file_manager/background.js b/ui/file_manager/integration_tests/file_manager/background.js
index fa3f246b..089b42e6 100644
--- a/ui/file_manager/integration_tests/file_manager/background.js
+++ b/ui/file_manager/integration_tests/file_manager/background.js
@@ -164,12 +164,12 @@
  * Entry set for Drive that includes team drives of various permissions and
  * nested files with various permissions.
  *
- * TODO(sashab): Add support for capabilities of Team Drive roots.
+ * TODO(sashab): Add support for capabilities of Shared Drive roots.
  *
  * @type {Array<TestEntryInfo>}
  * @const
  */
-const TEAM_DRIVE_ENTRY_SET = [
+const SHARED_DRIVE_ENTRY_SET = [
   ENTRIES.hello,
   ENTRIES.teamDriveA,
   ENTRIES.teamDriveAFile,
diff --git a/ui/file_manager/integration_tests/file_manager/context_menu.js b/ui/file_manager/integration_tests/file_manager/context_menu.js
index 8129f8c..75f19c56 100644
--- a/ui/file_manager/integration_tests/file_manager/context_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/context_menu.js
@@ -523,7 +523,7 @@
 
 /**
  * Tests that the specified menu item is in |expectedEnabledState| when the
- * context menu for the Team Drive root with name |teamDriveName| is opened in
+ * context menu for the Shared Drive root with name |teamDriveName| is opened in
  * the directory tree.
  *
  * TODO(sashab): Make this take a map of {commandId: expectedEnabledState}, and
@@ -541,7 +541,7 @@
 
   // Open Files App on Drive.
   const appId =
-      await setupAndWaitUntilReady(RootPath.DRIVE, [], TEAM_DRIVE_ENTRY_SET);
+      await setupAndWaitUntilReady(RootPath.DRIVE, [], SHARED_DRIVE_ENTRY_SET);
 
   // Focus the file list.
   chrome.test.assertTrue(!!await remoteCall.callRemoteTestUtil(
@@ -588,7 +588,7 @@
 
 /**
  * Tests that the context menu contains the correct items with the correct
- * enabled/disabled state if a Team Drive Root is selected.
+ * enabled/disabled state if a Shared Drive Root is selected.
  */
 testcase.checkContextMenuForTeamDriveRoot = () => {
   return checkTeamDriveContextMenuInTree('Team Drive A', {
diff --git a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
index 2784ce4..f21a1ac1 100644
--- a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
@@ -639,6 +639,46 @@
 };
 
 /**
+ * Tests context menu for a Zip root and a folder inside it.
+ */
+testcase.dirContextMenuZip = async () => {
+  const zipMenus = [
+    ['#unmount', true],
+  ];
+  const folderMenus = [
+    ['#cut', false],
+    ['#copy', true],
+    ['#paste-into-folder', false],
+    ['#rename', false],
+    ['#delete', false],
+    ['#new-folder', false],
+  ];
+
+  // Open Files app on Downloads containing a zip file.
+  const appId = await setupAndWaitUntilReady(
+      RootPath.DOWNLOADS, [ENTRIES.zipArchive], []);
+
+  // Select the zip file.
+  chrome.test.assertTrue(
+      !!await remoteCall.callRemoteTestUtil(
+          'selectFile', appId, ['archive.zip']),
+      'selectFile failed');
+
+  // Press the Enter key to mount the zip file.
+  const key = ['#file-list', 'Enter', false, false, false];
+  chrome.test.assertTrue(
+      !!await remoteCall.callRemoteTestUtil('fakeKeyDown', appId, key),
+      'fakeKeyDown failed');
+
+  // Check the context menu is on desired state.
+  await checkContextMenu(appId, '/archive.zip', zipMenus, true /* rootMenu */);
+
+  // Check the context menu for a folder inside the zip.
+  await checkContextMenu(
+      appId, '/archive.zip/folder', folderMenus, false /* rootMenu */);
+};
+
+/**
  * Tests context menu for Shortcut roots.
  */
 testcase.dirContextMenuShortcut = async () => {
@@ -884,6 +924,49 @@
       appId, '/fake-usb/DCIM', dcimFolderMenus, false /* rootMenu */);
 };
 
+/*
+ * Tests context menu for Mtp root and a folder inside it.
+ */
+testcase.dirContextMenuMtp = async () => {
+  const folderMenus = [
+    ['#cut', true],
+    ['#copy', true],
+    ['#paste-into-folder', false],
+    ['#rename', true],
+    ['#delete', true],
+    ['#new-folder', true],
+  ];
+
+  const mtpQuery = '#directory-tree [entry-label="fake-mtp"]';
+  const folderQuery = mtpQuery + ' [entry-label="A"]';
+
+  // Mount fake MTP volume.
+  await sendTestMessage({name: 'mountFakeMtp'});
+
+  // Open Files app on local Downloads.
+  const appId =
+      await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
+
+  // Select Recent root.
+  await remoteCall.waitAndClickElement(appId, mtpQuery + hasChildren);
+
+  // Right click on MTP root.
+  chrome.test.assertTrue(
+      !!await remoteCall.callRemoteTestUtil(
+          'fakeMouseRightClick', appId, [mtpQuery]),
+      'fakeMouseRightClick failed');
+
+  // Check that both menus are still hidden, since there is not context menu for
+  // MTP root.
+  await remoteCall.waitForElement(appId, '#roots-context-menu[hidden]');
+  await remoteCall.waitForElement(
+      appId, '#directory-tree-context-menu[hidden]');
+
+  // Check the context menu for a folder inside a MTP.
+  await checkContextMenu(
+      appId, '/fake-mtp/A', folderMenus, false /* rootMenu */);
+};
+
 /**
  * Tests context menu for FSP root and a folder inside it.
  */
diff --git a/ui/file_manager/integration_tests/file_manager/metadata.js b/ui/file_manager/integration_tests/file_manager/metadata.js
index 91da5f36..6faa401 100644
--- a/ui/file_manager/integration_tests/file_manager/metadata.js
+++ b/ui/file_manager/integration_tests/file_manager/metadata.js
@@ -67,14 +67,14 @@
 }
 
 /**
- * Creates a Team Drive.
- * @param {string} name Team Drive name.
+ * Creates a Shared Drive.
+ * @param {string} name Shared Drive name.
  * @return {TestEntryInfo}
  */
 function createTestTeamDrive(name) {
   return new TestEntryInfo({
     teamDriveName: name,
-    type: EntryType.TEAM_DRIVE,
+    type: EntryType.SHARED_DRIVE,
     capabilites: {
       canCopy: true,
       canDelete: true,
@@ -257,8 +257,8 @@
 /**
  * Measures the number of metadata operations generated for:
  *  - Opening Files app in My Drive, with 50 folders and 50 files.
- *  - Navigate to Team Drives, with 50 team drives.
- *  - Expand Team Drives to display the 50 team drives..
+ *  - Navigate to Shared Drives, with 50 team drives.
+ *  - Expand Shared Drives to display the 50 team drives..
  */
 testcase.metadataTeamDrives = async () => {
   const entries = [];
@@ -290,7 +290,7 @@
   await remoteCall.navigateWithDirectoryTree(
       appId, '/team_drives', 'Shared drives', 'drive');
 
-  // Expand Team Drives, because expanding might need metadata.
+  // Expand Shared Drives, because expanding might need metadata.
   const expandIcon = sharedDrivesTreeItem + ' > .tree-row > .expand-icon';
   await remoteCall.waitForElement(appId, expandIcon);
 
@@ -303,7 +303,7 @@
       sharedDrivesTreeItem + ' > .tree-children[expanded] > .tree-item';
   await remoteCall.waitForElement(appId, expandedSubItems);
 
-  // Get all Team Drives' children.
+  // Get all Shared Drives' children.
   const elements = await remoteCall.callRemoteTestUtil(
       'queryAllElements', appId,
       [sharedDrivesTreeItem + ' > .tree-children[expanded] > .tree-item']);
@@ -325,7 +325,7 @@
   // +  50 folders in My Drive.
   // +  50 team drives.
   // +   1 My Drive root.
-  // +   1 Team Drives root.
+  // +   1 Shared Drives root.
   // = 152
   chrome.test.assertEq(152, metadataStats.fullFetch);
 
diff --git a/ui/file_manager/integration_tests/file_manager/share_and_manage_dialog.js b/ui/file_manager/integration_tests/file_manager/share_and_manage_dialog.js
index 7c53bcd..14f36e2 100644
--- a/ui/file_manager/integration_tests/file_manager/share_and_manage_dialog.js
+++ b/ui/file_manager/integration_tests/file_manager/share_and_manage_dialog.js
@@ -15,7 +15,7 @@
     path, url, teamDrive = undefined) {
   // Open Files app on Drive.
   const appId = await setupAndWaitUntilReady(
-      RootPath.DRIVE, [], BASIC_DRIVE_ENTRY_SET.concat(TEAM_DRIVE_ENTRY_SET));
+      RootPath.DRIVE, [], BASIC_DRIVE_ENTRY_SET.concat(SHARED_DRIVE_ENTRY_SET));
 
   // Navigate to the specified team drive if one is specified.
   if (teamDrive !== undefined) {
@@ -76,7 +76,7 @@
     path, url, teamDrive = undefined) {
   // Open Files app on Drive.
   const appId = await setupAndWaitUntilReady(
-      RootPath.DRIVE, [], BASIC_DRIVE_ENTRY_SET.concat(TEAM_DRIVE_ENTRY_SET));
+      RootPath.DRIVE, [], BASIC_DRIVE_ENTRY_SET.concat(SHARED_DRIVE_ENTRY_SET));
 
   // Navigate to the specified team drive if one is specified.
   if (teamDrive !== undefined) {
@@ -195,7 +195,7 @@
 
   // Open Files app on Drive.
   const appId = await setupAndWaitUntilReady(
-      RootPath.DRIVE, [], BASIC_DRIVE_ENTRY_SET.concat(TEAM_DRIVE_ENTRY_SET));
+      RootPath.DRIVE, [], BASIC_DRIVE_ENTRY_SET.concat(SHARED_DRIVE_ENTRY_SET));
 
   // Navigate to the team drive.
   await remoteCall.navigateWithDirectoryTree(
diff --git a/ui/file_manager/integration_tests/file_manager/transfer.js b/ui/file_manager/integration_tests/file_manager/transfer.js
index 65d3b90..ee9b32b 100644
--- a/ui/file_manager/integration_tests/file_manager/transfer.js
+++ b/ui/file_manager/integration_tests/file_manager/transfer.js
@@ -108,36 +108,36 @@
   if (transferInfo.source.isTeamDrive) {
     srcContents =
         TestEntryInfo.getExpectedRows(transferInfo.source.initialEntries.filter(
-            entry => entry.type !== EntryType.TEAM_DRIVE &&
+            entry => entry.type !== EntryType.SHARED_DRIVE &&
                 entry.teamDriveName === transferInfo.source.volumeName));
   } else {
     srcContents =
         TestEntryInfo.getExpectedRows(transferInfo.source.initialEntries.filter(
-            entry => entry.type !== EntryType.TEAM_DRIVE &&
+            entry => entry.type !== EntryType.SHARED_DRIVE &&
                 entry.teamDriveName === ''));
   }
   const myDriveContent =
       TestEntryInfo.getExpectedRows(transferInfo.source.initialEntries.filter(
-          entry => entry.type !== EntryType.TEAM_DRIVE &&
+          entry => entry.type !== EntryType.SHARED_DRIVE &&
               entry.teamDriveName === ''));
 
   let dstContents;
   if (transferInfo.destination.isTeamDrive) {
     dstContents = TestEntryInfo.getExpectedRows(
         transferInfo.destination.initialEntries.filter(
-            entry => entry.type !== EntryType.TEAM_DRIVE &&
+            entry => entry.type !== EntryType.SHARED_DRIVE &&
                 entry.teamDriveName === transferInfo.destination.volumeName));
   } else {
     dstContents = TestEntryInfo.getExpectedRows(
         transferInfo.destination.initialEntries.filter(
-            entry => entry.type !== EntryType.TEAM_DRIVE &&
+            entry => entry.type !== EntryType.SHARED_DRIVE &&
                 entry.teamDriveName === ''));
   }
 
   const localFiles = BASIC_LOCAL_ENTRY_SET;
   const driveFiles = (transferInfo.source.isTeamDrive ||
                       transferInfo.destination.isTeamDrive) ?
-      TEAM_DRIVE_ENTRY_SET :
+      SHARED_DRIVE_ENTRY_SET :
       BASIC_DRIVE_ENTRY_SET;
 
   // Open files app.
@@ -242,7 +242,7 @@
       {volumeName: 'drive', initialEntries: BASIC_DRIVE_ENTRY_SET}),
 
   driveWithTeamDriveEntries: new TransferLocationInfo(
-      {volumeName: 'drive', initialEntries: TEAM_DRIVE_ENTRY_SET}),
+      {volumeName: 'drive', initialEntries: SHARED_DRIVE_ENTRY_SET}),
 
   downloads: new TransferLocationInfo(
       {volumeName: 'downloads', initialEntries: BASIC_LOCAL_ENTRY_SET}),
@@ -258,13 +258,13 @@
   driveTeamDriveA: new TransferLocationInfo({
     volumeName: 'Team Drive A',
     isTeamDrive: true,
-    initialEntries: TEAM_DRIVE_ENTRY_SET
+    initialEntries: SHARED_DRIVE_ENTRY_SET
   }),
 
   driveTeamDriveB: new TransferLocationInfo({
     volumeName: 'Team Drive B',
     isTeamDrive: true,
-    initialEntries: TEAM_DRIVE_ENTRY_SET
+    initialEntries: SHARED_DRIVE_ENTRY_SET
   }),
 
   my_files: new TransferLocationInfo({
diff --git a/ui/file_manager/integration_tests/file_manager/zip_files.js b/ui/file_manager/integration_tests/file_manager/zip_files.js
index da2eecd..6bdf50f 100644
--- a/ui/file_manager/integration_tests/file_manager/zip_files.js
+++ b/ui/file_manager/integration_tests/file_manager/zip_files.js
@@ -9,6 +9,18 @@
  */
 function getUnzippedFileListRowEntries() {
   return [
+    ['folder', '--', 'Folder', 'Dec 11, 2018, 5:08 PM'],
+    ['image.png', '272 bytes', 'PNG image', 'Sep 2, 2013, 11:01 PM'],
+    ['text.txt', '51 bytes', 'Plain text', 'Sep 2, 2013, 11:01 PM']
+  ];
+}
+
+/**
+ * Returns the expected file list row entries after opening (unzipping) the
+ * ENTRIES.zipArchiveEncrypted file list entry.
+ */
+function getUnzippedFileListRowEntriesEncrypted() {
+  return [
     ['image.png', '272 bytes', 'PNG image', 'Sep 2, 2013, 10:01 PM'],
     ['text.txt', '51 bytes', 'Plain text', 'Sep 2, 2013, 10:01 PM']
   ];
@@ -210,7 +222,7 @@
       'fakeKeyDown failed');
 
   // Check: the zip file content should be shown (unzip).
-  const files = getUnzippedFileListRowEntries();
+  const files = getUnzippedFileListRowEntriesEncrypted();
   await remoteCall.waitForFiles(appId, files, {'ignoreLastModifiedTime': true});
 
   // Select the text file in the ZIP file.
@@ -233,7 +245,7 @@
       'waitForAllPassphraseWindowsClosed failed');
 
   // Check: the zip file content should still be shown.
-  const files2 = getUnzippedFileListRowEntries();
+  const files2 = getUnzippedFileListRowEntriesEncrypted();
   await remoteCall.waitForFiles(appId, files, {'ignoreLastModifiedTime': true});
 };
 
diff --git a/ui/file_manager/integration_tests/test_util.js b/ui/file_manager/integration_tests/test_util.js
index f0e67c4b..ea9b570e77 100644
--- a/ui/file_manager/integration_tests/test_util.js
+++ b/ui/file_manager/integration_tests/test_util.js
@@ -271,7 +271,7 @@
 var EntryType = Object.freeze({
   FILE: 'file',
   DIRECTORY: 'directory',
-  TEAM_DRIVE: 'team_drive',
+  SHARED_DRIVE: 'team_drive',
   COMPUTER: 'Computer'
 });
 
@@ -736,7 +736,7 @@
     mimeType: 'application/x-zip',
     lastModifiedTime: 'Jan 1, 2014, 1:00 AM',
     nameText: 'archive.zip',
-    sizeText: '533 bytes',
+    sizeText: '743 bytes',
     typeText: 'Zip archive'
   }),
 
@@ -808,7 +808,7 @@
 
   // Team-drive entries.
   teamDriveA: new TestEntryInfo({
-    type: EntryType.TEAM_DRIVE,
+    type: EntryType.SHARED_DRIVE,
     teamDriveName: 'Team Drive A',
     capabilities: {
       canCopy: true,
@@ -867,7 +867,7 @@
   }),
 
   teamDriveB: new TestEntryInfo({
-    type: EntryType.TEAM_DRIVE,
+    type: EntryType.SHARED_DRIVE,
     teamDriveName: 'Team Drive B',
     capabilities: {
       canCopy: true,
diff --git a/ui/gl/init/create_gr_gl_interface.cc b/ui/gl/init/create_gr_gl_interface.cc
index 6ceaab45..fd2ad64 100644
--- a/ui/gl/init/create_gr_gl_interface.cc
+++ b/ui/gl/init/create_gr_gl_interface.cc
@@ -19,16 +19,6 @@
   return [func, api](Args... args) { return (api->*func)(args...); };
 }
 
-class ScopedProgressReporter {
- public:
-  ScopedProgressReporter(gl::ProgressReporter* progress_reporter)
-      : progress_reporter_(progress_reporter) {}
-  ~ScopedProgressReporter() { progress_reporter_->ReportProgress(); }
-
- private:
-  gl::ProgressReporter* progress_reporter_;
-};
-
 template <typename R, typename... Args>
 GrGLFunction<R GR_GL_FUNCTION_TYPE(Args...)> bind_slow(
     R(GL_BINDING_CALL* func)(Args...),
@@ -36,7 +26,7 @@
   if (!progress_reporter)
     return func;
   return [func, progress_reporter](Args... args) {
-    ScopedProgressReporter scoped_reporter(progress_reporter);
+    gl::ScopedProgressReporter scoped_reporter(progress_reporter);
     return func(args...);
   };
 }
@@ -63,7 +53,7 @@
     return bind_with_flush_on_mac(func);
   }
   return [func, progress_reporter](Args... args) {
-    ScopedProgressReporter scoped_reporter(progress_reporter);
+    gl::ScopedProgressReporter scoped_reporter(progress_reporter);
     return bind_with_flush_on_mac(func)(args...);
   };
 }
diff --git a/ui/gl/progress_reporter.h b/ui/gl/progress_reporter.h
index 5ebcfe0..165283e 100644
--- a/ui/gl/progress_reporter.h
+++ b/ui/gl/progress_reporter.h
@@ -17,6 +17,22 @@
   virtual void ReportProgress() = 0;
 };
 
+class ScopedProgressReporter {
+ public:
+  ScopedProgressReporter(ProgressReporter* progress_reporter)
+      : progress_reporter_(progress_reporter) {
+    if (progress_reporter_)
+      progress_reporter_->ReportProgress();
+  }
+  ~ScopedProgressReporter() {
+    if (progress_reporter_)
+      progress_reporter_->ReportProgress();
+  }
+
+ private:
+  ProgressReporter* progress_reporter_;
+};
+
 }  // namespace gl
 
 #endif  // UI_GL_PROGRESS_REPORTER_H_
diff --git a/ui/message_center/views/notification_header_view.cc b/ui/message_center/views/notification_header_view.cc
index 2fe528d13..b183797 100644
--- a/ui/message_center/views/notification_header_view.cc
+++ b/ui/message_center/views/notification_header_view.cc
@@ -110,7 +110,7 @@
 
 void ExpandButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   node_data->role = ax::mojom::Role::kButton;
-  node_data->SetName(tooltip_text());
+  node_data->SetName(GetTooltipText(gfx::Point()));
 }
 
 gfx::FontList GetHeaderTextFontList() {
diff --git a/ui/ozone/common/gpu/ozone_gpu_message_params.h b/ui/ozone/common/gpu/ozone_gpu_message_params.h
index 246a20c..cce7a669 100644
--- a/ui/ozone/common/gpu/ozone_gpu_message_params.h
+++ b/ui/ozone/common/gpu/ozone_gpu_message_params.h
@@ -57,7 +57,6 @@
   int64_t product_code = 0;
   int32_t year_of_manufacture = display::kInvalidYearOfManufacture;
   gfx::Size maximum_cursor_size;
-  bool has_associated_crtc = true;
 };
 
 struct OverlayCheck_Params {
diff --git a/ui/ozone/common/gpu/ozone_gpu_messages.h b/ui/ozone/common/gpu/ozone_gpu_messages.h
index 30cd521e..6a93dc2 100644
--- a/ui/ozone/common/gpu/ozone_gpu_messages.h
+++ b/ui/ozone/common/gpu/ozone_gpu_messages.h
@@ -67,7 +67,6 @@
   IPC_STRUCT_TRAITS_MEMBER(product_code)
   IPC_STRUCT_TRAITS_MEMBER(year_of_manufacture)
   IPC_STRUCT_TRAITS_MEMBER(maximum_cursor_size)
-  IPC_STRUCT_TRAITS_MEMBER(has_associated_crtc)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(display::GammaRampRGBEntry)
diff --git a/ui/ozone/common/linux/gbm_wrapper.cc b/ui/ozone/common/linux/gbm_wrapper.cc
index db7ff23..ce05d29 100644
--- a/ui/ozone/common/linux/gbm_wrapper.cc
+++ b/ui/ozone/common/linux/gbm_wrapper.cc
@@ -147,7 +147,7 @@
   gfx::NativePixmapHandle ExportHandle() const override {
     gfx::NativePixmapHandle handle;
     gfx::BufferFormat format = ui::GetBufferFormatFromFourCCFormat(format_);
-    // TODO(dcastagna): Use gbm_bo_get_num_planes once all the formats we use
+    // TODO(dcastagna): Use gbm_bo_get_plane_count once all the formats we use
     // are supported by gbm.
     for (size_t i = 0; i < gfx::NumberOfPlanesForBufferFormat(format); ++i) {
       base::ScopedFD scoped_fd(HANDLE_EINTR(dup(GetPlaneFd(i))));
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn
index 9b655da..d68ecdf 100644
--- a/ui/ozone/platform/drm/BUILD.gn
+++ b/ui/ozone/platform/drm/BUILD.gn
@@ -175,7 +175,6 @@
   sources = [
     "common/drm_overlay_manager_unittest.cc",
     "common/drm_util_unittest.cc",
-    "gpu/drm_gpu_display_manager_unittest.cc",
     "gpu/drm_overlay_validator_unittest.cc",
     "gpu/drm_window_unittest.cc",
     "gpu/hardware_display_controller_unittest.cc",
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index 732a931..76a6caf 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -33,11 +33,12 @@
                             outcome);
 }
 
-bool IsCrtcInUse(uint32_t crtc,
-                 const HardwareDisplayControllerInfos& displays) {
+bool IsCrtcInUse(
+    uint32_t crtc,
+    const std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>&
+        displays) {
   for (size_t i = 0; i < displays.size(); ++i) {
-    if (displays[i]->has_associated_crtc() &&
-        crtc == displays[i]->crtc()->crtc_id)
+    if (crtc == displays[i]->crtc()->crtc_id)
       return true;
   }
 
@@ -47,10 +48,12 @@
 // Return a CRTC compatible with |connector| and not already used in |displays|.
 // If there are multiple compatible CRTCs, the one that supports the majority of
 // planes will be returned.
-uint32_t GetCrtc(int fd,
-                 drmModeConnector* connector,
-                 drmModeRes* resources,
-                 const HardwareDisplayControllerInfos& displays) {
+uint32_t GetCrtc(
+    int fd,
+    drmModeConnector* connector,
+    drmModeRes* resources,
+    const std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>&
+        displays) {
   ScopedDrmPlaneResPtr plane_resources(drmModeGetPlaneResources(fd));
   std::vector<ScopedDrmPlanePtr> planes;
   for (uint32_t i = 0; i < plane_resources->count_planes; i++)
@@ -287,12 +290,11 @@
 HardwareDisplayControllerInfo::~HardwareDisplayControllerInfo() {
 }
 
-HardwareDisplayControllerInfos GetAvailableDisplayControllerInfos(
-    int fd,
-    bool* support_all_connectors) {
+std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>
+GetAvailableDisplayControllerInfos(int fd) {
   ScopedDrmResourcesPtr resources(drmModeGetResources(fd));
   DCHECK(resources) << "Failed to get DRM resources";
-  HardwareDisplayControllerInfos displays;
+  std::vector<std::unique_ptr<HardwareDisplayControllerInfo>> displays;
 
   std::vector<ScopedDrmConnectorPtr> connectors;
   std::vector<drmModeConnector*> available_connectors;
@@ -334,20 +336,12 @@
                             c1_crtcs != c2_crtcs;
                    });
 
-  if (support_all_connectors)
-    *support_all_connectors = true;
   for (auto* c : available_connectors) {
     uint32_t crtc_id = GetCrtc(fd, c, resources.get(), displays);
-    ScopedDrmCrtcPtr crtc;
+    if (!crtc_id)
+      continue;
 
-    if (crtc_id) {
-      crtc = ScopedDrmCrtcPtr(drmModeGetCrtc(fd, crtc_id));
-    } else {
-      // No available crtc for this connector.
-      if (support_all_connectors)
-        *support_all_connectors = false;
-    }
-
+    ScopedDrmCrtcPtr crtc(drmModeGetCrtc(fd, crtc_id));
     auto iter = std::find_if(connectors.begin(), connectors.end(),
                              [c](const ScopedDrmConnectorPtr& connector) {
                                return connector.get() == c;
@@ -394,8 +388,7 @@
     const drmModeModeInfo& mode = info->connector()->modes[i];
     modes.push_back(CreateDisplayMode(mode));
 
-    if (info->has_associated_crtc() && info->crtc()->mode_valid &&
-        SameMode(info->crtc()->mode, mode))
+    if (info->crtc()->mode_valid && SameMode(info->crtc()->mode, mode))
       *out_current_mode = modes.back().get();
 
     if (mode.type & DRM_MODE_TYPE_PREFERRED)
@@ -434,9 +427,8 @@
   const bool is_aspect_preserving_scaling =
       IsAspectPreserving(fd, info->connector());
   const bool has_color_correction_matrix =
-      info->has_associated_crtc() &&
-      (HasColorCorrectionMatrix(fd, info->crtc()) ||
-       HasPerPlaneColorCorrectionMatrix(fd, info->crtc()));
+      HasColorCorrectionMatrix(fd, info->crtc()) ||
+      HasPerPlaneColorCorrectionMatrix(fd, info->crtc());
   // On rk3399 we can set a color correction matrix that will be applied in
   // linear space. https://crbug.com/839020 to track if it will be possible to
   // disable the per-plane degamma/gamma.
@@ -486,7 +478,7 @@
       has_overscan, has_color_correction_matrix,
       color_correction_in_linear_space, display_color_space, display_name,
       sys_path, std::move(modes), edid, current_mode, native_mode, product_code,
-      year_of_manufacture, maximum_cursor_size, info->has_associated_crtc());
+      year_of_manufacture, maximum_cursor_size);
 }
 
 // TODO(rjkroege): Remove in a subsequent CL once Mojo IPC is used everywhere.
@@ -526,7 +518,6 @@
     p.product_code = d->product_code();
     p.year_of_manufacture = d->year_of_manufacture();
     p.maximum_cursor_size = d->maximum_cursor_size();
-    p.has_associated_crtc = d->has_associated_crtc();
 
     params.push_back(p);
   }
@@ -553,8 +544,7 @@
       params.color_correction_in_linear_space, params.color_space,
       params.display_name, params.sys_path, std::move(modes), params.edid,
       current_mode, native_mode, params.product_code,
-      params.year_of_manufacture, params.maximum_cursor_size,
-      params.has_associated_crtc);
+      params.year_of_manufacture, params.maximum_cursor_size);
 }
 
 int GetFourCCFormatForOpaqueFramebuffer(gfx::BufferFormat format) {
diff --git a/ui/ozone/platform/drm/common/drm_util.h b/ui/ozone/platform/drm/common/drm_util.h
index 7166f588..e70c23a8 100644
--- a/ui/ozone/platform/drm/common/drm_util.h
+++ b/ui/ozone/platform/drm/common/drm_util.h
@@ -30,10 +30,6 @@
 
 namespace ui {
 
-class HardwareDisplayControllerInfo;
-using HardwareDisplayControllerInfos =
-    std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>;
-
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
 enum class EdidColorSpaceChecksOutcome {
@@ -58,12 +54,8 @@
 
   drmModeConnector* connector() const { return connector_.get(); }
   drmModeCrtc* crtc() const { return crtc_.get(); }
-  ScopedDrmCrtcPtr release_crtc() { return std::move(crtc_); }
-  void set_crtc(ScopedDrmCrtcPtr crtc) { crtc_ = std::move(crtc); }
   size_t index() const { return index_; }
 
-  bool has_associated_crtc() const { return crtc_.get(); }
-
  private:
   ScopedDrmConnectorPtr connector_;
   ScopedDrmCrtcPtr crtc_;
@@ -73,11 +65,9 @@
 };
 
 // Looks-up and parses the native display configurations returning all available
-// displays. The boolean value indicates whether device has enough hardware
-// resource to support all of displays.
-HardwareDisplayControllerInfos GetAvailableDisplayControllerInfos(
-    int fd,
-    bool* support_all_connectors);
+// displays.
+std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>
+GetAvailableDisplayControllerInfos(int fd);
 
 bool SameMode(const drmModeModeInfo& lhs, const drmModeModeInfo& rhs);
 
diff --git a/ui/ozone/platform/drm/gpu/drm_device_manager.h b/ui/ozone/platform/drm/gpu/drm_device_manager.h
index 94680e1..f34170a3 100644
--- a/ui/ozone/platform/drm/gpu/drm_device_manager.h
+++ b/ui/ozone/platform/drm/gpu/drm_device_manager.h
@@ -49,12 +49,11 @@
 
   const DrmDeviceVector& GetDrmDevices() const;
 
- protected:
-  DrmDeviceVector devices_;
-
  private:
   const std::unique_ptr<DrmDeviceGenerator> drm_device_generator_;
 
+  DrmDeviceVector devices_;
+
   std::map<gfx::AcceleratedWidget, scoped_refptr<DrmDevice>> drm_device_map_;
 
   // This device represents the primary graphics device and is used when:
diff --git a/ui/ozone/platform/drm/gpu/drm_display.cc b/ui/ozone/platform/drm/gpu/drm_display.cc
index d7b771c6..f800442 100644
--- a/ui/ozone/platform/drm/gpu/drm_display.cc
+++ b/ui/ozone/platform/drm/gpu/drm_display.cc
@@ -92,7 +92,7 @@
     size_t device_index) {
   std::unique_ptr<display::DisplaySnapshot> params = CreateDisplaySnapshot(
       info, drm_->get_fd(), drm_->device_path(), device_index, origin_);
-  crtc_ = info->has_associated_crtc() ? info->crtc()->crtc_id : 0;
+  crtc_ = info->crtc()->crtc_id;
   connector_ = info->connector()->connector_id;
   display_id_ = params->display_id();
   modes_ = GetDrmModeVector(info->connector());
diff --git a/ui/ozone/platform/drm/gpu/drm_display.h b/ui/ozone/platform/drm/gpu/drm_display.h
index 44ec1821..16f2dae 100644
--- a/ui/ozone/platform/drm/gpu/drm_display.h
+++ b/ui/ozone/platform/drm/gpu/drm_display.h
@@ -39,10 +39,6 @@
   scoped_refptr<DrmDevice> drm() const { return drm_; }
   uint32_t crtc() const { return crtc_; }
   uint32_t connector() const { return connector_; }
-  void UpdateForTesting(uint32_t connector_id, uint32_t crtc_id) {
-    connector_ = connector_id;
-    crtc_ = crtc_id;
-  }
   const std::vector<drmModeModeInfo>& modes() const { return modes_; }
 
   std::unique_ptr<display::DisplaySnapshot> Update(
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
index 1de60873..232ae76 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
@@ -5,12 +5,12 @@
 #include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
 
 #include <stddef.h>
-#include <utility>
 
 #include "ui/display/types/display_mode.h"
 #include "ui/display/types/display_snapshot.h"
 #include "ui/display/types/gamma_ramp_rgb_entry.h"
 #include "ui/ozone/common/linux/drm_util_linux.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
 #include "ui/ozone/platform/drm/gpu/drm_display.h"
@@ -73,75 +73,28 @@
 }
 
 MovableDisplaySnapshots DrmGpuDisplayManager::GetDisplays() {
-  MovableDisplaySnapshots params_list;
   std::vector<std::unique_ptr<DrmDisplay>> old_displays;
   old_displays.swap(displays_);
-  HardwareDisplayControllerInfos old_hardware_infos;
-  old_hardware_infos.swap(hardware_infos_);
+  MovableDisplaySnapshots params_list;
 
   const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
   size_t device_index = 0;
   for (const auto& drm : devices) {
-    bool support_all_displays;
-    auto display_infos = QueryAvailableDisplayControllerInfos(
-        drm->get_fd(), &support_all_displays);
-
-    // Reallocate crtc with following strategy:
-    // (1) if there exists any display without associated crtc, the most
-    // recently connected display is denied the allocated crtc. Then the
-    // remaining displays are configured with previously assigned crtc which is
-    // recorded in |old_hardware_infos|. (2) if there are sufficient crtcs for
-    // each display, do the normal configuration.
-    if (!support_all_displays) {
-      for (auto& display_info : display_infos) {
-        // Connector id is unique. For each connected display,
-        // check whether it is recently connected with device or not.
-        auto hardware_info_it = std::find_if(
-            old_hardware_infos.begin(), old_hardware_infos.end(),
-            [&display_info](
-                std::unique_ptr<HardwareDisplayControllerInfo>& hardware_info) {
-              return display_info->connector()->connector_id ==
-                     hardware_info->connector()->connector_id;
-            });
-
-        if (hardware_info_it == old_hardware_infos.end()) {
-          // |display_info| corresponds to the most recently connected display.
-          display_info->set_crtc(nullptr);
-        } else {
-          // |display_info| corresponds to the display which has been connected
-          // with device before.
-          auto display_it =
-              std::find_if(old_displays.begin(), old_displays.end(),
-                           DisplayComparator(
-                               drm, (*hardware_info_it)->crtc()->crtc_id,
-                               (*hardware_info_it)->connector()->connector_id));
-          DCHECK(display_it != old_displays.end());
-          displays_.push_back(std::move(*display_it));
-          old_displays.erase(display_it);
-          display_info->set_crtc((*hardware_info_it)->release_crtc());
-          old_hardware_infos.erase(hardware_info_it);
-        }
+    auto display_infos = GetAvailableDisplayControllerInfos(drm->get_fd());
+    for (const auto& display_info : display_infos) {
+      auto it = std::find_if(
+          old_displays.begin(), old_displays.end(),
+          DisplayComparator(drm, display_info->crtc()->crtc_id,
+                            display_info->connector()->connector_id));
+      if (it != old_displays.end()) {
+        displays_.push_back(std::move(*it));
+        old_displays.erase(it);
+      } else {
+        displays_.push_back(std::make_unique<DrmDisplay>(screen_manager_, drm));
       }
-    } else {
-      for (auto& display_info : display_infos) {
-        auto it = std::find_if(
-            old_displays.begin(), old_displays.end(),
-            DisplayComparator(drm, display_info->crtc()->crtc_id,
-                              display_info->connector()->connector_id));
-        if (it != old_displays.end()) {
-          displays_.push_back(std::move(*it));
-          old_displays.erase(it);
-        } else {
-          displays_.push_back(
-              std::make_unique<DrmDisplay>(screen_manager_, drm));
-        }
-      }
+      params_list.push_back(
+          displays_.back()->Update(display_info.get(), device_index));
     }
-
-    MovableDisplaySnapshots sub_params_list =
-        GenerateParamsList(display_infos, device_index);
-    std::move(sub_params_list.begin(), sub_params_list.end(),
-              std::back_inserter(params_list));
     device_index++;
   }
 
@@ -273,35 +226,13 @@
   display->SetGammaCorrection(degamma_lut, gamma_lut);
 }
 
-HardwareDisplayControllerInfos
-DrmGpuDisplayManager::QueryAvailableDisplayControllerInfos(
-    int fd,
-    bool* support_all_displays) const {
-  return GetAvailableDisplayControllerInfos(fd, support_all_displays);
-}
-
-MovableDisplaySnapshots DrmGpuDisplayManager::GenerateParamsList(
-    HardwareDisplayControllerInfos& display_infos,
-    size_t device_index) {
-  MovableDisplaySnapshots params_list;
-  auto drm = drm_device_manager_->GetDrmDevices()[device_index];
-
-  // Generate |param_list| with updated |displays_|. Notice that the display
-  // connection without crtc allocated is not included in |displays_| but
-  // contained in |params_list|. Because Chrome side should be notified of it.
-  size_t display_index = 0;
-  for (auto& display_info : display_infos) {
-    if (display_info->has_associated_crtc()) {
-      params_list.push_back(
-          displays_[display_index++]->Update(display_info.get(), device_index));
-      hardware_infos_.push_back(std::move(display_info));
-    } else {
-      params_list.push_back(std::make_unique<DrmDisplay>(screen_manager_, drm)
-                                ->Update(display_info.get(), device_index));
-    }
+DrmDisplay* DrmGpuDisplayManager::FindDisplay(int64_t display_id) {
+  for (const auto& display : displays_) {
+    if (display->display_id() == display_id)
+      return display.get();
   }
 
-  return params_list;
+  return nullptr;
 }
 
 void DrmGpuDisplayManager::NotifyScreenManager(
@@ -328,13 +259,4 @@
   }
 }
 
-DrmDisplay* DrmGpuDisplayManager::FindDisplay(int64_t display_id) {
-  for (const auto& display : displays_) {
-    if (display->display_id() == display_id)
-      return display.get();
-  }
-
-  return nullptr;
-}
-
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
index d4f477d..4a7669fa 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
@@ -14,7 +14,6 @@
 #include "ui/gfx/native_widget_types.h"
 #include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
 #include "ui/ozone/platform/drm/common/display_types.h"
-#include "ui/ozone/platform/drm/common/drm_util.h"
 
 namespace display {
 class DisplayMode;
@@ -31,11 +30,7 @@
  public:
   DrmGpuDisplayManager(ScreenManager* screen_manager,
                        DrmDeviceManager* drm_device_manager);
-  virtual ~DrmGpuDisplayManager();
-
-  const HardwareDisplayControllerInfos& hardware_infos_for_test() const {
-    return hardware_infos_;
-  }
+  ~DrmGpuDisplayManager();
 
   // Returns a list of the connected displays. When this is called the list of
   // displays is refreshed.
@@ -60,35 +55,20 @@
       const std::vector<display::GammaRampRGBEntry>& degamma_lut,
       const std::vector<display::GammaRampRGBEntry>& gamma_lut);
 
- protected:
-  // Virtual for testing.
-  virtual HardwareDisplayControllerInfos QueryAvailableDisplayControllerInfos(
-      int fd,
-      bool* support_all_displays) const;
-
-  // Virtual for testing.
-  virtual MovableDisplaySnapshots GenerateParamsList(
-      HardwareDisplayControllerInfos& display_infos,
-      size_t device_index);
-
-  // Notify ScreenManager of all the displays that were present before the
-  // update but are gone after the update.
-  // Virtual for testing.
-  virtual void NotifyScreenManager(
-      const std::vector<std::unique_ptr<DrmDisplay>>& new_displays,
-      const std::vector<std::unique_ptr<DrmDisplay>>& old_displays) const;
-
-  // List of available displays and their controller information. Only displays
-  // having associated crtc are included.
-  std::vector<std::unique_ptr<DrmDisplay>> displays_;
-  HardwareDisplayControllerInfos hardware_infos_;
-
  private:
   DrmDisplay* FindDisplay(int64_t display_id);
 
+  // Notify ScreenManager of all the displays that were present before the
+  // update but are gone after the update.
+  void NotifyScreenManager(
+      const std::vector<std::unique_ptr<DrmDisplay>>& new_displays,
+      const std::vector<std::unique_ptr<DrmDisplay>>& old_displays) const;
+
   ScreenManager* const screen_manager_;         // Not owned.
   DrmDeviceManager* const drm_device_manager_;  // Not owned.
 
+  std::vector<std::unique_ptr<DrmDisplay>> displays_;
+
   DISALLOW_COPY_AND_ASSIGN(DrmGpuDisplayManager);
 };
 
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager_unittest.cc b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager_unittest.cc
deleted file mode 100644
index e53eb9dd..0000000
--- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager_unittest.cc
+++ /dev/null
@@ -1,267 +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 "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
-
-#include <xf86drm.h>
-#include <string>
-#include <unordered_map>
-#include <utility>
-
-#include "base/rand_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
-#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
-#include "ui/ozone/platform/drm/gpu/drm_display.h"
-#include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
-#include "ui/ozone/platform/drm/gpu/mock_gbm_device.h"
-#include "ui/ozone/platform/drm/gpu/screen_manager.h"
-
-namespace ui {
-
-namespace {
-
-constexpr int kMaxSupportDisplayNum = 2;
-
-class MockDrmDeviceManager : public DrmDeviceManager {
- public:
-  MockDrmDeviceManager(std::unique_ptr<DrmDeviceGenerator> drm_device_generator)
-      : DrmDeviceManager(std::move(drm_device_generator)) {}
-  ~MockDrmDeviceManager() = default;
-
-  void AddDrmDeviceForTesting(scoped_refptr<DrmDevice> drm_device) {
-    devices_.push_back(drm_device);
-  }
-};
-
-class DrmGpuDisplayManagerTestHelper {
- public:
-  DrmGpuDisplayManagerTestHelper(size_t max_support_display_num) {
-    for (size_t i = 1; i <= max_support_display_num; i++)
-      crtc_resources_.push_back(i);
-  }
-
-  HardwareDisplayControllerInfos GetAvailableDisplayControllerInfos(
-      int fd,
-      bool* support_all_displays);
-
-  const std::unordered_map<uint32_t, uint32_t>& intermediate_mappings() const {
-    return intermediate_mappings_;
-  }
-
-  void AddDisplay(uint32_t connector_id) {
-    connector_resources_.push_back(connector_id);
-  }
-
- private:
-  // Record the mappings between connector and crtc before crtc reallocation.
-  std::unordered_map<uint32_t, uint32_t> intermediate_mappings_;
-
-  std::vector<uint32_t> connector_resources_;
-  std::vector<uint32_t> crtc_resources_;
-};
-
-HardwareDisplayControllerInfos
-DrmGpuDisplayManagerTestHelper::GetAvailableDisplayControllerInfos(
-    int fd,
-    bool* support_all_displays) {
-  intermediate_mappings_.clear();
-  *support_all_displays = true;
-  HardwareDisplayControllerInfos ret;
-  auto crtc_iter = crtc_resources_.rbegin();
-
-  // Pair connector with crtc in a dummy way. If there is no available crtc, the
-  // assigned crtc should be a null pointer. Use reverse iterator to ensure that
-  // the most recent connected display has associated crtc before crtc
-  // reallocation. It can help to test the code block of crtc reallocation.
-  for (auto connector_iter = connector_resources_.rbegin();
-       connector_iter != connector_resources_.rend(); connector_iter++) {
-    drmModeConnectorPtr connector = nullptr;
-    connector = static_cast<drmModeConnectorPtr>(drmMalloc(sizeof(*connector)));
-    DCHECK(connector);
-    connector->connector_id = *connector_iter;
-    connector->encoders = nullptr;
-    connector->prop_values = nullptr;
-    connector->props = nullptr;
-    connector->modes = nullptr;
-
-    drmModeCrtcPtr crtc = nullptr;
-    if (crtc_iter != crtc_resources_.rend()) {
-      crtc = static_cast<drmModeCrtcPtr>(drmMalloc(sizeof(*crtc)));
-      DCHECK(crtc);
-      crtc->crtc_id = *crtc_iter;
-      intermediate_mappings_[connector->connector_id] = crtc->crtc_id;
-      crtc_iter++;
-    } else {
-      *support_all_displays = false;
-    }
-
-    ret.push_back(std::make_unique<HardwareDisplayControllerInfo>(
-        ScopedDrmConnectorPtr(connector), ScopedDrmCrtcPtr(crtc),
-        connector_iter - connector_resources_.rbegin()));
-  }
-
-  return ret;
-}
-
-class MockDrmGpuDisplayManager : public DrmGpuDisplayManager {
- public:
-  MockDrmGpuDisplayManager(
-      std::unique_ptr<DrmGpuDisplayManagerTestHelper> test_helper,
-      ScreenManager* screen_manager,
-      MockDrmDeviceManager* device_manager)
-      : DrmGpuDisplayManager(screen_manager, device_manager),
-        test_helper_(std::move(test_helper)) {}
-
-  // Get the mappings between connector and crtc before crtc reallocation.
-  const std::unordered_map<uint32_t, uint32_t>& intermediate_mappings() const {
-    return test_helper_->intermediate_mappings();
-  }
-
-  void AddDisplay(uint32_t connector_id) {
-    test_helper_->AddDisplay(connector_id);
-  }
-
- private:
-  HardwareDisplayControllerInfos QueryAvailableDisplayControllerInfos(
-      int fd,
-      bool* support_all_displays) const override {
-    return test_helper_->GetAvailableDisplayControllerInfos(
-        fd, support_all_displays);
-  }
-
-  MovableDisplaySnapshots GenerateParamsList(
-      HardwareDisplayControllerInfos& display_infos,
-      size_t device_index) override;
-
-  // In test do nothing.
-  void NotifyScreenManager(
-      const std::vector<std::unique_ptr<DrmDisplay>>& new_displays,
-      const std::vector<std::unique_ptr<DrmDisplay>>& old_displays)
-      const override {}
-
-  std::unique_ptr<DrmGpuDisplayManagerTestHelper> test_helper_;
-};
-
-MovableDisplaySnapshots MockDrmGpuDisplayManager::GenerateParamsList(
-    HardwareDisplayControllerInfos& display_infos,
-    size_t device_index) {
-  MovableDisplaySnapshots params_list;
-
-  size_t display_index = 0;
-  for (auto& display_info : display_infos) {
-    if (display_info->has_associated_crtc()) {
-      displays_[display_index++]->UpdateForTesting(
-          display_info->connector()->connector_id,
-          display_info->crtc()->crtc_id);
-      hardware_infos_.push_back(std::move(display_info));
-    }
-
-    // Generate dummy snapshots.
-    params_list.push_back(std::make_unique<display::DisplaySnapshot>(
-        /*display_id=*/display::kInvalidDisplayId, /*origin=*/gfx::Point(),
-        /*physical_size=*/gfx::Size(),
-        /*type=*/display::DisplayConnectionType(),
-        /*is_aspect_preserving_scaling=*/false, /*has_overscan=*/false,
-        /*has_color_correction_matrix=*/false,
-        /*color_correction_in_linear_space=*/false,
-        /*color_space=*/gfx::ColorSpace(), /*display_name=*/std::string(),
-        /*sys_path=*/base::FilePath(),
-        /*modes=*/display::DisplaySnapshot::DisplayModeList(),
-        /*edid=*/std::vector<uint8_t>(), /*current_mode=*/nullptr,
-        /*native_mode=*/nullptr, /*product_code=*/0,
-        /*year_of_manufacture=*/display::kInvalidYearOfManufacture,
-        /*maximum_cursor_size=*/gfx::Size(), /*has_associated_crtc=*/true));
-  }
-
-  return params_list;
-}
-
-}  // namespace
-
-class DrmGpuDisplayManagerTest : public testing::Test {
- public:
-  DrmGpuDisplayManagerTest() {}
-  ~DrmGpuDisplayManagerTest() override {}
-
-  // From testing::Test.
-  void SetUp() override;
-  void TearDown() override {
-    screen_manager_.reset();
-    device_manager_.reset();
-    drm_gpu_display_manager_.reset();
-  }
-
- protected:
-  std::unique_ptr<ScreenManager> screen_manager_;
-  std::unique_ptr<MockDrmDeviceManager> device_manager_;
-  std::unique_ptr<MockDrmGpuDisplayManager> drm_gpu_display_manager_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DrmGpuDisplayManagerTest);
-};
-
-void DrmGpuDisplayManagerTest::SetUp() {
-  auto gbm = std::make_unique<ui::MockGbmDevice>();
-  MockDrmDevice* device = new MockDrmDevice(std::move(gbm));
-  scoped_refptr<DrmDevice> drm_device(device);
-
-  device_manager_.reset(new MockDrmDeviceManager(nullptr));
-  device_manager_->AddDrmDeviceForTesting(drm_device);
-  screen_manager_.reset(new ScreenManager());
-
-  size_t max_support_display_num = kMaxSupportDisplayNum;
-  drm_gpu_display_manager_.reset(new MockDrmGpuDisplayManager(
-      std::make_unique<DrmGpuDisplayManagerTestHelper>(max_support_display_num),
-      screen_manager_.get(), device_manager_.get()));
-}
-
-TEST_F(DrmGpuDisplayManagerTest, GetDisplays) {
-  const uint32_t display_id1 = 1, display_id2 = 2, display_id3 = 3;
-
-  // Connect two displays with device then update |hardware_infos_|.
-  drm_gpu_display_manager_->AddDisplay(display_id1);
-  drm_gpu_display_manager_->AddDisplay(display_id2);
-  MovableDisplaySnapshots display_snapshots1 =
-      drm_gpu_display_manager_->GetDisplays();
-  EXPECT_EQ(display_snapshots1.size(), 2u);
-
-  std::unordered_map<uint32_t, uint32_t> connector_crtc_map;
-
-  // Record the mappings between connector and crtc.
-  const auto& hardware_infos =
-      drm_gpu_display_manager_->hardware_infos_for_test();
-  size_t connected_display_size = hardware_infos.size();
-  for (const auto& hardware_info : hardware_infos) {
-    uint32_t connector_id = hardware_info->connector()->connector_id,
-             crtc_id = hardware_info->crtc()->crtc_id;
-    connector_crtc_map[connector_id] = crtc_id;
-  }
-
-  drm_gpu_display_manager_->AddDisplay(display_id3);
-  MovableDisplaySnapshots display_snapshots2 =
-      drm_gpu_display_manager_->GetDisplays();
-  EXPECT_EQ(display_snapshots2.size(), 3u);
-
-  // The third display has associated crtc before crtc reallocation.
-  const auto& iter =
-      drm_gpu_display_manager_->intermediate_mappings().find(display_id3);
-  EXPECT_EQ(iter->second, 2u);
-
-  // Note that device only supports at most two displays. So the third one
-  // should not have associated crtc after reallocation (see comments in
-  // DrmGpuDisplayManager::GetDisplays for more details). |hardware_infos|,
-  // recording information of connected displays, should not change.
-  const auto& updated_hardware_infos =
-      drm_gpu_display_manager_->hardware_infos_for_test();
-  EXPECT_EQ(connected_display_size, updated_hardware_infos.size());
-  for (const auto& hardware_info : updated_hardware_infos) {
-    uint32_t connector_id = hardware_info->connector()->connector_id,
-             crtc_id = hardware_info->crtc()->crtc_id;
-
-    uint32_t expected_crtc_id = connector_crtc_map[connector_id];
-    EXPECT_EQ(crtc_id, expected_crtc_id);
-  }
-}
-}  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_display_host_manager.cc b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
index cbed94f..6aaee53 100644
--- a/ui/ozone/platform/drm/host/drm_display_host_manager.cc
+++ b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
@@ -146,8 +146,8 @@
   proxy_->RegisterHandlerForDrmDisplayHostManager(this);
   proxy_->AddGpuThreadObserver(this);
 
-  const auto& display_infos = GetAvailableDisplayControllerInfos(
-      primary_drm_device_handle_->fd(), nullptr);
+  auto display_infos =
+      GetAvailableDisplayControllerInfos(primary_drm_device_handle_->fd());
   has_dummy_display_ = !display_infos.empty();
   for (const auto& display_info : display_infos) {
     displays_.push_back(std::make_unique<DrmDisplayHost>(
diff --git a/ui/ozone/platform/headless/headless_native_display_delegate.cc b/ui/ozone/platform/headless/headless_native_display_delegate.cc
index 0fa6a8d..a4ca1d32 100644
--- a/ui/ozone/platform/headless/headless_native_display_delegate.cc
+++ b/ui/ozone/platform/headless/headless_native_display_delegate.cc
@@ -38,8 +38,7 @@
       next_display_id(), gfx::Point(0, 0), kDefaultWindowSize,
       display::DisplayConnectionType::DISPLAY_CONNECTION_TYPE_NONE, false,
       false, false, false, gfx::ColorSpace(), "", base::FilePath(),
-      std::move(modes), std::vector<uint8_t>(), mode, mode, 0, 0, gfx::Size(),
-      /*has_associated_crtc=*/true);
+      std::move(modes), std::vector<uint8_t>(), mode, mode, 0, 0, gfx::Size());
 
   for (display::NativeDisplayObserver& observer : observers_)
     observer.OnConfigurationChanged();
diff --git a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
index 5d46385..c2c1ea08 100644
--- a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
+++ b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
@@ -133,7 +133,7 @@
   gfx::NativePixmapHandle handle;
   gfx::BufferFormat format = GetBufferFormat();
 
-  // TODO(dcastagna): Use gbm_bo_get_num_planes once all the formats we use are
+  // TODO(dcastagna): Use gbm_bo_get_plane_count once all the formats we use are
   // supported by gbm.
   const size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format);
   std::vector<base::ScopedFD> scoped_fds(num_planes);
diff --git a/ui/ozone/platform/wayland/wayland_connection.cc b/ui/ozone/platform/wayland/wayland_connection.cc
index 05e23b5..2044e47 100644
--- a/ui/ozone/platform/wayland/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/wayland_connection.cc
@@ -7,6 +7,9 @@
 #include <xdg-shell-unstable-v5-client-protocol.h>
 #include <xdg-shell-unstable-v6-client-protocol.h>
 
+#include <algorithm>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -36,15 +39,7 @@
 constexpr uint32_t kMaxDeviceManagerVersion = 3;
 constexpr uint32_t kMaxWpPresentationVersion = 1;
 constexpr uint32_t kMaxTextInputManagerVersion = 1;
-
 constexpr uint32_t kMinWlOutputVersion = 2;
-
-std::unique_ptr<WaylandDataSource> CreateWaylandDataSource(
-    WaylandDataDeviceManager* data_device_manager,
-    WaylandConnection* connection) {
-  wl_data_source* data_source = data_device_manager->CreateSource();
-  return std::make_unique<WaylandDataSource>(data_source, connection);
-}
 }  // namespace
 
 WaylandConnection::WaylandConnection()
@@ -259,11 +254,11 @@
 void WaylandConnection::OfferClipboardData(
     const PlatformClipboard::DataMap& data_map,
     PlatformClipboard::OfferDataClosure callback) {
-  if (!data_source_) {
-    data_source_ = CreateWaylandDataSource(data_device_manager_.get(), this);
-    data_source_->WriteToClipboard(data_map);
+  if (!clipboard_data_source_) {
+    clipboard_data_source_ = data_device_manager_->CreateSource();
+    clipboard_data_source_->WriteToClipboard(data_map);
   }
-  data_source_->UpdateDataMap(data_map);
+  clipboard_data_source_->UpdateDataMap(data_map);
   std::move(callback).Run();
 }
 
@@ -279,7 +274,7 @@
 }
 
 bool WaylandConnection::IsSelectionOwner() {
-  return !!data_source_;
+  return !!clipboard_data_source_;
 }
 
 void WaylandConnection::SetSequenceNumberUpdateCb(
@@ -311,13 +306,11 @@
 
 void WaylandConnection::StartDrag(const ui::OSExchangeData& data,
                                   int operation) {
-  if (!drag_data_source_) {
-    drag_data_source_ =
-        CreateWaylandDataSource(data_device_manager_.get(), this);
-  }
-  drag_data_source_->Offer(data);
-  drag_data_source_->SetAction(operation);
-  data_device_->StartDrag(*(drag_data_source_->data_source()), data);
+  if (!dragdrop_data_source_)
+    dragdrop_data_source_ = data_device_manager_->CreateSource();
+  dragdrop_data_source_->Offer(data);
+  dragdrop_data_source_->SetAction(operation);
+  data_device_->StartDrag(*(dragdrop_data_source_->data_source()), data);
 }
 
 void WaylandConnection::FinishDragSession(uint32_t dnd_action,
@@ -325,7 +318,7 @@
   if (source_window)
     source_window->OnDragSessionClose(dnd_action);
   data_device_->ResetSourceData();
-  drag_data_source_.reset();
+  dragdrop_data_source_.reset();
 }
 
 void WaylandConnection::DeliverDragData(const std::string& mime_type,
@@ -354,8 +347,9 @@
 }
 
 void WaylandConnection::DataSourceCancelled() {
-  SetClipboardData(std::string(), std::string());
-  data_source_.reset();
+  DCHECK(clipboard_data_source_);
+  SetClipboardData({}, {});
+  clipboard_data_source_.reset();
 }
 
 void WaylandConnection::SetClipboardData(const std::string& contents,
diff --git a/ui/ozone/platform/wayland/wayland_connection.h b/ui/ozone/platform/wayland/wayland_connection.h
index 86228edc6..a6bc2b4 100644
--- a/ui/ozone/platform/wayland/wayland_connection.h
+++ b/ui/ozone/platform/wayland/wayland_connection.h
@@ -6,6 +6,9 @@
 #define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_CONNECTION_H_
 
 #include <map>
+#include <memory>
+#include <string>
+#include <vector>
 
 #include "base/files/file.h"
 #include "base/message_loop/message_pump_libevent.h"
@@ -32,7 +35,7 @@
 class WaylandOutputManager;
 class WaylandWindow;
 
-// TODO: factor out PlatformClipboard to a separate class.
+// TODO(crbug.com/942203): factor out PlatformClipboard to a separate class.
 class WaylandConnection : public PlatformEventSource,
                           public PlatformClipboard,
                           public ozone::mojom::WaylandConnection,
@@ -114,7 +117,7 @@
   WaylandPointer* pointer() const { return pointer_.get(); }
 
   WaylandDataSource* drag_data_source() const {
-    return drag_data_source_.get();
+    return dragdrop_data_source_.get();
   }
 
   WaylandOutputManager* wayland_output_manager() const {
@@ -237,8 +240,8 @@
 
   std::unique_ptr<WaylandDataDeviceManager> data_device_manager_;
   std::unique_ptr<WaylandDataDevice> data_device_;
-  std::unique_ptr<WaylandDataSource> data_source_;
-  std::unique_ptr<WaylandDataSource> drag_data_source_;
+  std::unique_ptr<WaylandDataSource> clipboard_data_source_;
+  std::unique_ptr<WaylandDataSource> dragdrop_data_source_;
   std::unique_ptr<WaylandKeyboard> keyboard_;
   std::unique_ptr<WaylandOutputManager> wayland_output_manager_;
   std::unique_ptr<WaylandPointer> pointer_;
diff --git a/ui/ozone/platform/wayland/wayland_cursor.cc b/ui/ozone/platform/wayland/wayland_cursor.cc
index db2d5913..725e70c 100644
--- a/ui/ozone/platform/wayland/wayland_cursor.cc
+++ b/ui/ozone/platform/wayland/wayland_cursor.cc
@@ -4,14 +4,10 @@
 
 #include "ui/ozone/platform/wayland/wayland_cursor.h"
 
-#include <sys/mman.h>
-#include <vector>
-
 #include "base/memory/shared_memory.h"
-#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/ozone/platform/wayland/wayland_connection.h"
-#include "ui/ozone/platform/wayland/wayland_pointer.h"
 #include "ui/ozone/platform/wayland/wayland_util.h"
 
 namespace ui {
@@ -23,15 +19,16 @@
 // static
 void WaylandCursor::OnBufferRelease(void* data, wl_buffer* buffer) {
   auto* cursor = static_cast<WaylandCursor*>(data);
-  DCHECK(cursor->buffers_.count(buffer) > 0);
+  DCHECK_GT(cursor->buffers_.count(buffer), 0u);
   cursor->buffers_.erase(buffer);
 }
 
 void WaylandCursor::Init(wl_pointer* pointer, WaylandConnection* connection) {
   DCHECK(connection);
+  DCHECK(connection->shm());
+  DCHECK(connection->compositor());
 
   input_pointer_ = pointer;
-
   shm_ = connection->shm();
   pointer_surface_.reset(
       wl_compositor_create_surface(connection->compositor()));
@@ -69,6 +66,8 @@
 
   wl_pointer_set_cursor(input_pointer_, serial, pointer_surface_.get(),
                         hotspot.x(), hotspot.y());
+  wl_surface_damage(pointer_surface_.get(), 0, 0, image_size.width(),
+                    image_size.height());
   wl_surface_attach(pointer_surface_.get(), buffer.get(), 0, 0);
   wl_surface_commit(pointer_surface_.get());
 
diff --git a/ui/ozone/platform/wayland/wayland_cursor.h b/ui/ozone/platform/wayland/wayland_cursor.h
index e7e8368d..00de27e 100644
--- a/ui/ozone/platform/wayland/wayland_cursor.h
+++ b/ui/ozone/platform/wayland/wayland_cursor.h
@@ -6,16 +6,18 @@
 #define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_CURSOR_H_
 
 #include <wayland-client.h>
+
 #include <memory>
 #include <utility>
 #include <vector>
 
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
-#include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "ui/ozone/platform/wayland/wayland_object.h"
 
+class SkBitmap;
+
 namespace base {
 class SharedMemory;
 }
diff --git a/ui/ozone/platform/wayland/wayland_data_device.cc b/ui/ozone/platform/wayland/wayland_data_device.cc
index 92e96de0..2c0143eb 100644
--- a/ui/ozone/platform/wayland/wayland_data_device.cc
+++ b/ui/ozone/platform/wayland/wayland_data_device.cc
@@ -4,6 +4,8 @@
 
 #include "ui/ozone/platform/wayland/wayland_data_device.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/memory/shared_memory.h"
 #include "base/strings/string16.h"
@@ -59,18 +61,13 @@
     AddStringToOSExchangeData(data, os_exchange_data);
     return;
   }
-
-  // TODO(jkim): Handle other mime types as well.
+  // TODO(crbug.com/875164): Fix mime types support.
   NOTREACHED();
 }
 
 }  // namespace
 
 // static
-const wl_callback_listener WaylandDataDevice::callback_listener_ = {
-    WaylandDataDevice::SyncCallback,
-};
-
 WaylandDataDevice::WaylandDataDevice(WaylandConnection* connection,
                                      wl_data_device* data_device)
     : data_device_(data_device),
@@ -99,10 +96,10 @@
 
   // Ensure there is not pending operation to be performed by the compositor,
   // otherwise read(..) can block awaiting data to be sent to pipe.
-  read_from_fd_closure_ =
+  deferred_read_closure_ =
       base::BindOnce(&WaylandDataDevice::ReadClipboardDataFromFD,
                      base::Unretained(this), std::move(fd), mime_type);
-  RegisterSyncCallback();
+  RegisterDeferredReadCallback();
 }
 
 void WaylandDataDevice::RequestDragData(
@@ -116,10 +113,10 @@
 
   // Ensure there is not pending operation to be performed by the compositor,
   // otherwise read(..) can block awaiting data to be sent to pipe.
-  read_from_fd_closure_ = base::BindOnce(&WaylandDataDevice::ReadDragDataFromFD,
-                                         base::Unretained(this), std::move(fd),
-                                         std::move(callback));
-  RegisterSyncCallback();
+  deferred_read_closure_ = base::BindOnce(
+      &WaylandDataDevice::ReadDragDataFromFD, base::Unretained(this),
+      std::move(fd), std::move(callback));
+  RegisterDeferredReadCallback();
 }
 
 void WaylandDataDevice::DeliverDragData(const std::string& mime_type,
@@ -195,13 +192,6 @@
   std::move(callback).Run(contents);
 }
 
-void WaylandDataDevice::RegisterSyncCallback() {
-  DCHECK(!sync_callback_);
-  sync_callback_.reset(wl_display_sync(connection_->display()));
-  wl_callback_add_listener(sync_callback_.get(), &callback_listener_, this);
-  wl_display_flush(connection_->display());
-}
-
 void WaylandDataDevice::ReadDataFromFD(base::ScopedFD fd,
                                        std::string* contents) {
   DCHECK(contents);
@@ -250,33 +240,25 @@
   self->drag_offer_ = std::move(self->new_offer_);
   self->window_ = window;
 
-  // TODO(jkim): Set mime type the client can accept. Now it sets all mime types
-  // offered because current implementation doesn't decide action based on mime
-  // type.
-  const std::vector<std::string>& mime_types =
-      self->drag_offer_->GetAvailableMimeTypes();
-  for (auto mime : mime_types)
+  // TODO(crbug.com/875164): Set mime type the client can accept. Now it sets
+  // all mime types offered because current implementation doesn't decide
+  // action based on mime type.
+  self->unprocessed_mime_types_.clear();
+  for (auto mime : self->drag_offer_->GetAvailableMimeTypes()) {
+    self->unprocessed_mime_types_.push_back(mime);
     self->drag_offer_->Accept(serial, mime);
-
-  std::copy(mime_types.begin(), mime_types.end(),
-            std::insert_iterator<std::list<std::string>>(
-                self->unprocessed_mime_types_,
-                self->unprocessed_mime_types_.begin()));
+  }
 
   int operation = GetOperation(self->drag_offer_->source_actions(),
                                self->drag_offer_->dnd_action());
   gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y));
 
-  // If it has |source_data_|, it means that the dragging is started from the
-  // same window and it doesn't need to read the data through Wayland.
-  if (self->source_data_) {
-    std::unique_ptr<OSExchangeData> data = std::make_unique<OSExchangeData>(
-        self->source_data_->provider().Clone());
-    self->window_->OnDragEnter(point, std::move(data), operation);
-    return;
-  }
-
-  self->window_->OnDragEnter(point, nullptr, operation);
+  // If |source_data_| is set, it means that dragging is started from the
+  // same window and it's not needed to read data through Wayland.
+  std::unique_ptr<OSExchangeData> pdata;
+  if (!self->IsDraggingExternalData())
+    pdata.reset(new OSExchangeData(self->source_data_->provider().Clone()));
+  self->window_->OnDragEnter(point, std::move(pdata), operation);
 }
 
 void WaylandDataDevice::OnMotion(void* data,
@@ -303,20 +285,26 @@
     LOG(ERROR) << "Failed to get window.";
     return;
   }
-
-  // Creates buffer to receive data from Wayland.
-  self->received_data_ = std::make_unique<OSExchangeData>(
-      std::make_unique<OSExchangeDataProviderAura>());
-
-  // Starts to read the data on Drop event because read(..) API blocks
-  // awaiting data to be sent to pipe if we try to read the data on OnEnter.
-  // 'Weston' also reads data on OnDrop event and other examples do as well.
-  self->HandleNextMimeType();
-
-  // In order to guarantee all data received, it sets
-  // |is_handling_dropped_data_| and defers OnLeave event handling if it gets
-  // OnLeave event before completing to read the data.
-  self->is_handling_dropped_data_ = true;
+  if (!self->IsDraggingExternalData()) {
+    // When the drag session started from a chromium window, source_data_
+    // already holds the data and already forwarded it to delegate through
+    // OnDragEnter, so at this point (onDragDrop) the delegate expects a
+    // nullptr and the data will be read internally with no need to read it
+    // through Wayland pipe and so on.
+    self->HandleReceivedData(nullptr);
+  } else {
+    // Creates buffer to receive data from Wayland.
+    self->received_data_.reset(
+        new OSExchangeData(std::make_unique<OSExchangeDataProviderAura>()));
+    // In order to guarantee all data received, it sets
+    // |is_handling_dropped_data_| and defers OnLeave event handling if it gets
+    // OnLeave event before completing to read the data.
+    self->is_handling_dropped_data_ = true;
+    // Starts to read the data on Drop event because read(..) API blocks
+    // awaiting data to be sent to pipe if we try to read the data on OnEnter.
+    // 'Weston' also reads data on OnDrop event and other examples do as well.
+    self->HandleUnprocessedMimeTypes();
+  }
 }
 
 void WaylandDataDevice::OnLeave(void* data, wl_data_device* data_device) {
@@ -363,15 +351,25 @@
   self->selection_offer_->EnsureTextMimeTypeIfNeeded();
 }
 
-void WaylandDataDevice::SyncCallback(void* data,
-                                     struct wl_callback* cb,
-                                     uint32_t time) {
-  WaylandDataDevice* data_device = static_cast<WaylandDataDevice*>(data);
-  DCHECK(data_device);
+void WaylandDataDevice::RegisterDeferredReadCallback() {
+  static const wl_callback_listener kDeferredReadListener = {
+      WaylandDataDevice::DeferredReadCallback};
 
-  std::move(data_device->read_from_fd_closure_).Run();
-  DCHECK(data_device->read_from_fd_closure_.is_null());
-  data_device->sync_callback_.reset();
+  DCHECK(!deferred_read_callback_);
+  deferred_read_callback_.reset(wl_display_sync(connection_->display()));
+  wl_callback_add_listener(deferred_read_callback_.get(),
+                           &kDeferredReadListener, this);
+  wl_display_flush(connection_->display());
+}
+
+void WaylandDataDevice::DeferredReadCallback(void* data,
+                                             struct wl_callback* cb,
+                                             uint32_t time) {
+  auto* data_device = static_cast<WaylandDataDevice*>(data);
+  DCHECK(data_device);
+  DCHECK(!data_device->deferred_read_closure_.is_null());
+  std::move(data_device->deferred_read_closure_).Run();
+  data_device->deferred_read_callback_.reset();
 }
 
 void WaylandDataDevice::CreateDragImage(const SkBitmap* bitmap) {
@@ -396,24 +394,37 @@
   wl_surface_commit(icon_surface_.get());
 }
 
+void WaylandDataDevice::HandleUnprocessedMimeTypes() {
+  std::string mime_type = SelectNextMimeType();
+  if (mime_type.empty()) {
+    HandleReceivedData(std::move(received_data_));
+  } else {
+    RequestDragData(mime_type,
+                    base::BindOnce(&WaylandDataDevice::OnDragDataReceived,
+                                   base::Unretained(this)));
+  }
+}
+
 void WaylandDataDevice::OnDragDataReceived(const std::string& contents) {
   if (!contents.empty()) {
     AddToOSExchangeData(contents, unprocessed_mime_types_.front(),
                         received_data_.get());
   }
 
-  unprocessed_mime_types_.erase(unprocessed_mime_types_.begin());
+  unprocessed_mime_types_.pop_front();
 
-  // Read next data corresponding to the mime type.
-  HandleNextMimeType();
+  // Continue reading data for other negotiated mime types.
+  HandleUnprocessedMimeTypes();
 }
 
-void WaylandDataDevice::OnDragDataCollected() {
+void WaylandDataDevice::HandleReceivedData(
+    std::unique_ptr<ui::OSExchangeData> received_data) {
+  // TODO(crbug.com/875164): Fix mime types support.
   unprocessed_mime_types_.clear();
-  window_->OnDragDrop(std::move(received_data_));
+
+  window_->OnDragDrop(std::move(received_data));
   drag_offer_->FinishOffer();
   is_handling_dropped_data_ = false;
-
   HandleDeferredLeaveIfNeeded();
 }
 
@@ -424,21 +435,10 @@
         !received_data_->HasString()) {
       return mime_type;
     }
-    // TODO(jkim): Handle other mime types as well.
-    unprocessed_mime_types_.erase(unprocessed_mime_types_.begin());
+    // TODO(crbug.com/875164): Fix mime types support.
+    unprocessed_mime_types_.pop_front();
   }
-  return std::string();
-}
-
-void WaylandDataDevice::HandleNextMimeType() {
-  std::string mime_type = SelectNextMimeType();
-  if (!mime_type.empty()) {
-    RequestDragData(mime_type,
-                    base::BindOnce(&WaylandDataDevice::OnDragDataReceived,
-                                   base::Unretained(this)));
-  } else {
-    OnDragDataCollected();
-  }
+  return {};
 }
 
 void WaylandDataDevice::SetOperation(const int operation) {
diff --git a/ui/ozone/platform/wayland/wayland_data_device.h b/ui/ozone/platform/wayland/wayland_data_device.h
index c3953993..7a0f36a 100644
--- a/ui/ozone/platform/wayland/wayland_data_device.h
+++ b/ui/ozone/platform/wayland/wayland_data_device.h
@@ -6,8 +6,11 @@
 #define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_DATA_DEVICE_H_
 
 #include <wayland-client.h>
+
 #include <list>
+#include <memory>
 #include <string>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/files/scoped_file.h"
@@ -67,12 +70,13 @@
       base::ScopedFD fd,
       base::OnceCallback<void(const std::string&)> callback);
 
-  // Registers display sync callback. Once it's called, it's reset.
-  void RegisterSyncCallback();
-
   // Helper function to read data from fd.
   void ReadDataFromFD(base::ScopedFD fd, std::string* contents);
 
+  // If source_data_ is not set, data is being dragged from an external
+  // application (non-chromium).
+  bool IsDraggingExternalData() const { return !source_data_; }
+
   // If OnLeave event occurs while it's reading drag data, it defers handling
   // it. Once reading data is completed, it's handled.
   void HandleDeferredLeaveIfNeeded();
@@ -108,20 +112,30 @@
                           wl_data_device* data_device,
                           wl_data_offer* id);
 
-  static void SyncCallback(void* data, struct wl_callback* cb, uint32_t time);
+  // Registers DeferredReadCallback as display sync callback listener, to
+  // ensure there is no pending operation to be performed by the compositor,
+  // otherwise read(..) could block awaiting data to be sent to pipe. It is
+  // reset once it's called.
+  void RegisterDeferredReadCallback();
+  static void DeferredReadCallback(void* data,
+                                   struct wl_callback* cb,
+                                   uint32_t time);
 
   bool CreateSHMBuffer(const gfx::Size& size);
   void CreateDragImage(const SkBitmap* bitmap);
 
   void OnDragDataReceived(const std::string& contents);
-  void OnDragDataCollected();
 
+  // HandleUnprocessedMimeTypes asynchronously request and read data for every
+  // negotiated mime type, one after another (OnDragDataReceived calls back
+  // HandleUnprocessedMimeTypes so it finish only when there's no more items in
+  // unprocessed_mime_types_ vector). HandleReceivedData is called once the
+  // process is finished.
+  void HandleUnprocessedMimeTypes();
+  void HandleReceivedData(std::unique_ptr<ui::OSExchangeData> received_data);
   // Returns the next MIME type to be received from the source process, or an
   // empty string if there are no more interesting MIME types left to process.
   std::string SelectNextMimeType();
-  // If it has |unprocessed_mime_types_|, it takes the mime type in front and
-  // requests the data corresponding to the mime type to wayland.
-  void HandleNextMimeType();
 
   // Set drag operation decided by client.
   void SetOperation(const int operation);
@@ -151,9 +165,8 @@
   WaylandWindow* window_ = nullptr;
 
   // Make sure server has written data on the pipe, before block on read().
-  static const wl_callback_listener callback_listener_;
-  base::OnceClosure read_from_fd_closure_;
-  wl::Object<wl_callback> sync_callback_;
+  base::OnceClosure deferred_read_closure_;
+  wl::Object<wl_callback> deferred_read_callback_;
 
   bool is_handling_dropped_data_ = false;
   bool is_leaving_ = false;
diff --git a/ui/ozone/platform/wayland/wayland_data_device_manager.cc b/ui/ozone/platform/wayland/wayland_data_device_manager.cc
index 4c20f3fb..25737b78 100644
--- a/ui/ozone/platform/wayland/wayland_data_device_manager.cc
+++ b/ui/ozone/platform/wayland/wayland_data_device_manager.cc
@@ -5,6 +5,7 @@
 #include "ui/ozone/platform/wayland/wayland_data_device_manager.h"
 
 #include "ui/ozone/platform/wayland/wayland_connection.h"
+#include "ui/ozone/platform/wayland/wayland_data_source.h"
 
 namespace ui {
 
@@ -24,8 +25,10 @@
                                                 connection_->seat());
 }
 
-wl_data_source* WaylandDataDeviceManager::CreateSource() {
-  return wl_data_device_manager_create_data_source(device_manager_.get());
+std::unique_ptr<WaylandDataSource> WaylandDataDeviceManager::CreateSource() {
+  wl_data_source* data_source =
+      wl_data_device_manager_create_data_source(device_manager_.get());
+  return std::make_unique<WaylandDataSource>(data_source, connection_);
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_data_device_manager.h b/ui/ozone/platform/wayland/wayland_data_device_manager.h
index f444d7d..18f5b07 100644
--- a/ui/ozone/platform/wayland/wayland_data_device_manager.h
+++ b/ui/ozone/platform/wayland/wayland_data_device_manager.h
@@ -7,12 +7,15 @@
 
 #include <wayland-client.h>
 
+#include <memory>
+
 #include "base/macros.h"
 #include "ui/ozone/platform/wayland/wayland_object.h"
 
 namespace ui {
 
 class WaylandConnection;
+class WaylandDataSource;
 
 class WaylandDataDeviceManager {
  public:
@@ -21,7 +24,7 @@
   ~WaylandDataDeviceManager();
 
   wl_data_device* GetDevice();
-  wl_data_source* CreateSource();
+  std::unique_ptr<WaylandDataSource> CreateSource();
 
  private:
   wl::Object<wl_data_device_manager> device_manager_;
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 6de181ea..0479858 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -764,8 +764,8 @@
       <message name="IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME" desc="The spoken feedback text for the settings button in a notification. Usually 'button' is suffixed to this text automatically.">
         Notification settings
       </message>
-      <message name="IDS_MESSAGE_NOTIFICATION_SEND_TAB_TO_SELF_DEVICE_INFO_PREFIX" desc="For SEND_TAB_TO_SELF notification, this will be a prefix of device info.">
-        Shared from
+      <message name="IDS_MESSAGE_NOTIFICATION_SEND_TAB_TO_SELF_DEVICE_INFO" desc="The message text for the notification when the user receive a shared tab from another device. It shows the name of the device which shared the tab.">
+        Shared from <ph name="DEVICE_NAME">$1<ex>Ted's Pixel 2</ex></ph>
       </message>
       <if expr="chromeos">
         <message name="IDS_MESSAGE_CENTER_NOTIFIER_SCREENSHOT_NAME" desc="The name of screenshot notifier that is a system component">
diff --git a/ui/strings/ui_strings_grd/IDS_MESSAGE_NOTIFICATION_SEND_TAB_TO_SELF_DEVICE_INFO.png.sha1 b/ui/strings/ui_strings_grd/IDS_MESSAGE_NOTIFICATION_SEND_TAB_TO_SELF_DEVICE_INFO.png.sha1
new file mode 100644
index 0000000..5d2dedf
--- /dev/null
+++ b/ui/strings/ui_strings_grd/IDS_MESSAGE_NOTIFICATION_SEND_TAB_TO_SELF_DEVICE_INFO.png.sha1
@@ -0,0 +1 @@
+60ed4d4970955144f221df97537f358d427b6ef8
\ No newline at end of file
diff --git a/ui/views/cocoa/drag_drop_client_mac.h b/ui/views/cocoa/drag_drop_client_mac.h
index 163d84d0..d952728 100644
--- a/ui/views/cocoa/drag_drop_client_mac.h
+++ b/ui/views/cocoa/drag_drop_client_mac.h
@@ -72,7 +72,7 @@
   BridgedNativeWidgetImpl* bridge_;  // Weak. Owns |this|.
 
   // The closure for the drag and drop's run loop.
-  base::Closure quit_closure_;
+  base::OnceClosure quit_closure_;
 
   // Whether |this| is the source of current dragging session.
   bool is_drag_source_ = false;
diff --git a/ui/views/cocoa/drag_drop_client_mac.mm b/ui/views/cocoa/drag_drop_client_mac.mm
index 19640e0..7826c2f2 100644
--- a/ui/views/cocoa/drag_drop_client_mac.mm
+++ b/ui/views/cocoa/drag_drop_client_mac.mm
@@ -119,10 +119,8 @@
   is_drag_source_ = false;
 
   // Allow a test to invoke EndDrag() without spinning the nested run loop.
-  if (!quit_closure_.is_null()) {
-    quit_closure_.Run();
-    quit_closure_.Reset();
-  }
+  if (!quit_closure_.is_null())
+    std::move(quit_closure_).Run();
 }
 
 void DragDropClientMac::DragExit() {
diff --git a/ui/views/controls/image_view_base.cc b/ui/views/controls/image_view_base.cc
index 0a7d593..bb9dbd82 100644
--- a/ui/views/controls/image_view_base.cc
+++ b/ui/views/controls/image_view_base.cc
@@ -68,7 +68,7 @@
 }
 
 base::string16 ImageViewBase::GetTooltipText(const gfx::Point& p) const {
-  return tooltip_text();
+  return tooltip_text_;
 }
 
 gfx::Size ImageViewBase::CalculatePreferredSize() const {
diff --git a/ui/views/controls/image_view_base.h b/ui/views/controls/image_view_base.h
index d94c62448..9e68282c 100644
--- a/ui/views/controls/image_view_base.h
+++ b/ui/views/controls/image_view_base.h
@@ -39,11 +39,10 @@
   void SetVerticalAlignment(Alignment va);
   Alignment GetVerticalAlignment() const;
 
-  // Set / Get the tooltip text.
+  // Set the tooltip text.
   void set_tooltip_text(const base::string16& tooltip) {
     tooltip_text_ = tooltip;
   }
-  const base::string16& tooltip_text() const { return tooltip_text_; }
 
   // Set / Get the accessible name text.
   void SetAccessibleName(const base::string16& name);
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 8a302b5..df410ba 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -129,33 +129,37 @@
 }
 
 // Recurses through the child views of |view| returning the first view starting
-// at |start| that is focusable. A value of -1 for |start| indicates to start at
-// the first view (if |forward| is false, iterating starts at the last view). If
-// |forward| is true the children are considered first to last, otherwise last
-// to first.
-View* GetFirstFocusableView(View* view, int start, bool forward) {
-  if (forward) {
-    for (int i = start == -1 ? 0 : start; i < view->child_count(); ++i) {
-      View* deepest = GetFirstFocusableView(view->child_at(i), -1, forward);
-      if (deepest)
-        return deepest;
-    }
-  } else {
-    for (int i = start == -1 ? view->child_count() - 1 : start; i >= 0; --i) {
-      View* deepest = GetFirstFocusableView(view->child_at(i), -1, forward);
-      if (deepest)
-        return deepest;
-    }
+// at |start| that is focusable. Children are considered first to last.
+// TODO(https://crbug.com/942358): This can also return |view|, which seems
+// incorrect.
+View* GetFirstFocusableViewForward(View* view, int start) {
+  for (int i = start; i < view->child_count(); ++i) {
+    View* deepest = GetFirstFocusableViewForward(view->child_at(i), 0);
+    if (deepest)
+      return deepest;
   }
-  return view->IsFocusable() ? view : NULL;
+  return view->IsFocusable() ? view : nullptr;
+}
+
+// As GetFirstFocusableViewForward(), but children are considered last to first.
+View* GetFirstFocusableViewBackward(View* view, int start) {
+  for (int i = start; i >= 0; --i) {
+    View* deepest = GetFirstFocusableViewBackward(view->child_at(i),
+                                                  view->child_count() - 1);
+    if (deepest)
+      return deepest;
+  }
+  return view->IsFocusable() ? view : nullptr;
 }
 
 // Returns the first child of |start| that is focusable.
 View* GetInitialFocusableView(View* start, bool forward) {
-  return GetFirstFocusableView(start, -1, forward);
+  return forward
+             ? GetFirstFocusableViewForward(start, 0)
+             : GetFirstFocusableViewBackward(start, start->child_count() - 1);
 }
 
-// Returns the next view after |start_at| that is focusable. Returns NULL if
+// Returns the next view after |start_at| that is focusable. Returns null if
 // there are no focusable children of |ancestor| after |start_at|.
 View* GetNextFocusableView(View* ancestor, View* start_at, bool forward) {
   DCHECK(ancestor->Contains(start_at));
@@ -163,15 +167,13 @@
   do {
     View* new_parent = parent->parent();
     int index = new_parent->GetIndexOf(parent);
-    index += forward ? 1 : -1;
-    if (forward || index != -1) {
-      View* next = GetFirstFocusableView(new_parent, index, forward);
-      if (next)
-        return next;
-    }
+    View* next = forward ? GetFirstFocusableViewForward(new_parent, index + 1)
+                         : GetFirstFocusableViewBackward(new_parent, index - 1);
+    if (next)
+      return next;
     parent = new_parent;
   } while (parent != ancestor);
-  return NULL;
+  return nullptr;
 }
 
 #if defined(OS_WIN)
diff --git a/ui/views/controls/table/table_view.cc b/ui/views/controls/table/table_view.cc
index 0826696f..d782796 100644
--- a/ui/views/controls/table/table_view.cc
+++ b/ui/views/controls/table/table_view.cc
@@ -551,14 +551,17 @@
 }
 
 base::string16 TableView::GetTooltipText(const gfx::Point& p) const {
-  base::string16 tooltip;
-  GetTooltipImpl(p, &tooltip, NULL);
-  return tooltip;
-}
+  const int row = p.y() / row_height_;
+  if (row < 0 || row >= RowCount() || visible_columns_.empty())
+    return base::string16();
 
-bool TableView::GetTooltipTextOrigin(const gfx::Point& p,
-                                     gfx::Point* loc) const {
-  return GetTooltipImpl(p, NULL, loc);
+  const int x = GetMirroredXInView(p.x());
+  const int column = GetClosestVisibleColumnIndex(this, x);
+  if (x < visible_columns_[column].x ||
+      x > (visible_columns_[column].x + visible_columns_[column].width))
+    return base::string16();
+
+  return model_->GetText(ViewToModel(row), visible_columns_[column].column.id);
 }
 
 void TableView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
@@ -1169,41 +1172,6 @@
   return range;
 }
 
-bool TableView::GetTooltipImpl(const gfx::Point& location,
-                               base::string16* tooltip,
-                               gfx::Point* tooltip_origin) const {
-  const int row = location.y() / row_height_;
-  if (row < 0 || row >= RowCount() || visible_columns_.empty())
-    return false;
-
-  const int x = GetMirroredXInView(location.x());
-  const int column = GetClosestVisibleColumnIndex(this, x);
-  if (x < visible_columns_[column].x ||
-      x > (visible_columns_[column].x + visible_columns_[column].width))
-    return false;
-
-  const base::string16 text(model_->GetText(ViewToModel(row),
-                                      visible_columns_[column].column.id));
-  if (text.empty())
-    return false;
-
-  gfx::Rect cell_bounds(GetCellBounds(row, column));
-  AdjustCellBoundsForText(column, &cell_bounds);
-  const int right = std::min(GetVisibleBounds().right(), cell_bounds.right());
-  if (right > cell_bounds.x() &&
-      gfx::GetStringWidth(text, font_list_) <= (right - cell_bounds.x()))
-    return false;
-
-  if (tooltip)
-    *tooltip = text;
-  if (tooltip_origin) {
-    tooltip_origin->SetPoint(
-        cell_bounds.x(),
-        cell_bounds.y() + (row_height_ - font_list_.GetHeight()) / 2);
-  }
-  return true;
-}
-
 void TableView::UpdateVirtualAccessibilityChildren() {
   GetViewAccessibility().RemoveAllVirtualChildViews();
   if (!RowCount() || visible_columns_.empty()) {
diff --git a/ui/views/controls/table/table_view.h b/ui/views/controls/table/table_view.h
index 4dcd8ca..76980a3 100644
--- a/ui/views/controls/table/table_view.h
+++ b/ui/views/controls/table/table_view.h
@@ -207,8 +207,6 @@
   bool OnMousePressed(const ui::MouseEvent& event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
   base::string16 GetTooltipText(const gfx::Point& p) const override;
-  bool GetTooltipTextOrigin(const gfx::Point& p,
-                            gfx::Point* loc) const override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   bool HandleAccessibleAction(const ui::AXActionData& action_data) override;
 
@@ -327,13 +325,6 @@
   // 1.
   GroupRange GetGroupRange(int model_index) const;
 
-  // Used by both GetTooltipText methods. Returns true if there is a tooltip and
-  // sets |tooltip| and/or |tooltip_origin| as appropriate, each of which may be
-  // NULL.
-  bool GetTooltipImpl(const gfx::Point& location,
-                      base::string16* tooltip,
-                      gfx::Point* tooltip_origin) const;
-
   // Updates a set of accessibility views that expose the visible table contents
   // to assistive software.
   void UpdateVirtualAccessibilityChildren();
diff --git a/ui/views/examples/examples_with_content_main_exe.cc b/ui/views/examples/examples_with_content_main_exe.cc
index 3506d74..f2d84ae 100644
--- a/ui/views/examples/examples_with_content_main_exe.cc
+++ b/ui/views/examples/examples_with_content_main_exe.cc
@@ -46,7 +46,7 @@
   ui::ViewsContentClient views_content_client(argc, argv);
 #endif
 
-  views_content_client.set_task(base::Bind(
+  views_content_client.set_on_pre_main_message_loop_run_callback(base::BindOnce(
       &ShowContentExampleWindow, base::Unretained(&views_content_client)));
   return views_content_client.RunMain();
 }
diff --git a/ui/views/mus/drag_interactive_uitest.cc b/ui/views/mus/drag_interactive_uitest.cc
index d125aa8..755af06d 100644
--- a/ui/views/mus/drag_interactive_uitest.cc
+++ b/ui/views/mus/drag_interactive_uitest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
@@ -49,13 +51,13 @@
   TargetView() : dropped_(false) {}
   ~TargetView() override {}
 
-  void WaitForDropped(base::Closure quit_closure) {
+  void WaitForDropped(base::OnceClosure quit_closure) {
     if (dropped_) {
-      quit_closure.Run();
+      std::move(quit_closure).Run();
       return;
     }
 
-    quit_closure_ = quit_closure;
+    quit_closure_ = std::move(quit_closure);
   }
 
   // views::View overrides:
@@ -73,7 +75,7 @@
   int OnPerformDrop(const ui::DropTargetEvent& event) override {
     dropped_ = true;
     if (quit_closure_)
-      quit_closure_.Run();
+      std::move(quit_closure_).Run();
     return ui::DragDropTypes::DRAG_MOVE;
   }
 
@@ -82,7 +84,7 @@
  private:
   bool dropped_;
 
-  base::Closure quit_closure_;
+  base::OnceClosure quit_closure_;
 
   DISALLOW_COPY_AND_ASSIGN(TargetView);
 };
@@ -115,7 +117,7 @@
 // consists of callback functions which will perform an action after the
 // previous action has completed.
 void DragTest_Part3(int64_t display_id,
-                    const base::Closure& quit_closure,
+                    base::RepeatingClosure quit_closure,
                     bool result) {
   EXPECT_TRUE(result);
   quit_closure.Run();
@@ -123,7 +125,7 @@
 
 void DragTest_Part2(ws::mojom::EventInjector* event_injector,
                     int64_t display_id,
-                    const base::Closure& quit_closure,
+                    base::RepeatingClosure quit_closure,
                     bool result) {
   EXPECT_TRUE(result);
   if (!result)
@@ -131,12 +133,12 @@
 
   event_injector->InjectEvent(
       display_id, CreateMouseUpEvent(30, 30),
-      base::BindOnce(&DragTest_Part3, display_id, quit_closure));
+      base::BindOnce(&DragTest_Part3, display_id, std::move(quit_closure)));
 }
 
 void DragTest_Part1(ws::mojom::EventInjector* event_injector,
                     int64_t display_id,
-                    const base::Closure& quit_closure,
+                    base::RepeatingClosure quit_closure,
                     bool result) {
   EXPECT_TRUE(result);
   if (!result)
@@ -145,7 +147,7 @@
   event_injector->InjectEvent(
       display_id, CreateMouseMoveEvent(30, 30),
       base::BindOnce(&DragTest_Part2, base::Unretained(event_injector),
-                     display_id, quit_closure));
+                     display_id, std::move(quit_closure)));
 }
 
 TEST_F(DragTestInteractive, DragTest) {
diff --git a/ui/views/test/platform_test_helper.cc b/ui/views/test/platform_test_helper.cc
index 46e1f3c..94bda695 100644
--- a/ui/views/test/platform_test_helper.cc
+++ b/ui/views/test/platform_test_helper.cc
@@ -4,6 +4,8 @@
 
 #include "ui/views/test/platform_test_helper.h"
 
+#include <utility>
+
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -17,7 +19,7 @@
 namespace views {
 namespace {
 
-PlatformTestHelper::Factory test_helper_factory;
+PlatformTestHelper::Factory g_test_helper_factory;
 
 }  // namespace
 
@@ -25,16 +27,16 @@
   ui::TerminateContextFactoryForTests();
 }
 
-void PlatformTestHelper::set_factory(const Factory& factory) {
-  DCHECK_NE(factory.is_null(), test_helper_factory.is_null());
-  test_helper_factory = factory;
+void PlatformTestHelper::set_factory(Factory factory) {
+  DCHECK_NE(factory.is_null(), g_test_helper_factory.is_null());
+  g_test_helper_factory = std::move(factory);
 }
 
 // static
 std::unique_ptr<PlatformTestHelper> PlatformTestHelper::Create() {
-  return !test_helper_factory.is_null()
-             ? test_helper_factory.Run()
-             : base::WrapUnique(new PlatformTestHelper);
+  return g_test_helper_factory.is_null()
+             ? base::WrapUnique(new PlatformTestHelper)
+             : g_test_helper_factory.Run();
 }
 
 #if defined(USE_AURA)
diff --git a/ui/views/test/platform_test_helper.h b/ui/views/test/platform_test_helper.h
index 7d6670b..63ce0b45f 100644
--- a/ui/views/test/platform_test_helper.h
+++ b/ui/views/test/platform_test_helper.h
@@ -22,11 +22,13 @@
 
 class PlatformTestHelper {
  public:
-  using Factory = base::Callback<std::unique_ptr<PlatformTestHelper>(void)>;
-  PlatformTestHelper() {}
+  using Factory =
+      base::RepeatingCallback<std::unique_ptr<PlatformTestHelper>(void)>;
+
+  PlatformTestHelper() = default;
   virtual ~PlatformTestHelper();
 
-  static void set_factory(const Factory& factory);
+  static void set_factory(Factory factory);
   static std::unique_ptr<PlatformTestHelper> Create();
 
   // Called once the ViewsTestHelper has been created, but before SetUp() is
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 6f8ef16..610ff68 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -1255,10 +1255,6 @@
   return base::string16();
 }
 
-bool View::GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* loc) const {
-  return false;
-}
-
 // Context menus ---------------------------------------------------------------
 
 void View::ShowContextMenu(const gfx::Point& p,
diff --git a/ui/views/view.h b/ui/views/view.h
index 63acf6a2..96a0c004 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -1024,11 +1024,6 @@
   // |p| provides the coordinates of the mouse (relative to this view).
   virtual base::string16 GetTooltipText(const gfx::Point& p) const;
 
-  // Returns the location (relative to this View) for the text on the tooltip
-  // to display. If false is returned (the default), the tooltip is placed at
-  // a default position.
-  virtual bool GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* loc) const;
-
   // Context menus -------------------------------------------------------------
 
   // Sets the ContextMenuController. Setting this to non-null makes the View
diff --git a/ui/views/views_delegate.cc b/ui/views/views_delegate.cc
index 7252607..df95cd6 100644
--- a/ui/views/views_delegate.cc
+++ b/ui/views/views_delegate.cc
@@ -123,7 +123,7 @@
 
 #if defined(OS_WIN)
 int ViewsDelegate::GetAppbarAutohideEdges(HMONITOR monitor,
-                                          const base::Closure& callback) {
+                                          base::OnceClosure callback) {
   return EDGE_BOTTOM;
 }
 #endif
diff --git a/ui/views/views_delegate.h b/ui/views/views_delegate.h
index 41728e3..bc6d63d 100644
--- a/ui/views/views_delegate.h
+++ b/ui/views/views_delegate.h
@@ -187,7 +187,7 @@
   //
   // The return value is a bitmask of AppbarAutohideEdge.
   virtual int GetAppbarAutohideEdges(HMONITOR monitor,
-                                     const base::Closure& callback);
+                                     base::OnceClosure callback);
 #endif
 
  protected:
diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
index 6bf4875..7c65d27 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
@@ -261,7 +261,7 @@
 }
 
 void DesktopScreenX11::RestartDelayedConfigurationTask() {
-  delayed_configuration_task_.Reset(base::Bind(
+  delayed_configuration_task_.Reset(base::BindOnce(
       &DesktopScreenX11::UpdateDisplays, weak_factory_.GetWeakPtr()));
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, delayed_configuration_task_.callback());
diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11.h b/ui/views/widget/desktop_aura/desktop_screen_x11.h
index 164ad57..06ca082 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_x11.h
+++ b/ui/views/widget/desktop_aura/desktop_screen_x11.h
@@ -98,7 +98,7 @@
 
   // The task to delay configuring outputs.  We delay updating the
   // display so we can coalesce events.
-  base::CancelableCallback<void()> delayed_configuration_task_;
+  base::CancelableOnceCallback<void()> delayed_configuration_task_;
 
   display::DisplayChangeNotifier change_notifier_;
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index d460531..e31a2704 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -2387,9 +2387,9 @@
 }
 
 void DesktopWindowTreeHostX11::RestartDelayedResizeTask() {
-  delayed_resize_task_.Reset(
-      base::Bind(&DesktopWindowTreeHostX11::DelayedResize,
-                 close_widget_factory_.GetWeakPtr(), bounds_in_pixels_.size()));
+  delayed_resize_task_.Reset(base::BindOnce(
+      &DesktopWindowTreeHostX11::DelayedResize,
+      close_widget_factory_.GetWeakPtr(), bounds_in_pixels_.size()));
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, delayed_resize_task_.callback());
 }
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
index 43bd1f4e..8fdc869 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
@@ -448,7 +448,7 @@
   // Captures system key events when keyboard lock is requested.
   std::unique_ptr<ui::KeyboardHook> keyboard_hook_;
 
-  base::CancelableCallback<void()> delayed_resize_task_;
+  base::CancelableOnceCallback<void()> delayed_resize_task_;
 
   std::unique_ptr<aura::ScopedWindowTargeter> targeter_for_modal_;
 
diff --git a/ui/views/widget/widget_interactive_uitest.cc b/ui/views/widget/widget_interactive_uitest.cc
index 7f5fa4e..4c5b247 100644
--- a/ui/views/widget/widget_interactive_uitest.cc
+++ b/ui/views/widget/widget_interactive_uitest.cc
@@ -5,6 +5,7 @@
 #include <stddef.h>
 
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/command_line.h"
 #include "base/location.h"
 #include "base/macros.h"
@@ -56,16 +57,22 @@
 // receives a mouse-release event.
 class ExitLoopOnRelease : public View {
  public:
-  ExitLoopOnRelease() {}
-  ~ExitLoopOnRelease() override {}
+  explicit ExitLoopOnRelease(base::OnceClosure quit_closure)
+      : quit_closure_(std::move(quit_closure)) {
+    DCHECK(quit_closure_);
+  }
+
+  ~ExitLoopOnRelease() override = default;
 
  private:
   // View:
   void OnMouseReleased(const ui::MouseEvent& event) override {
     GetWidget()->Close();
-    base::RunLoop::QuitCurrentDeprecated();
+    std::move(quit_closure_).Run();
   }
 
+  base::OnceClosure quit_closure_;
+
   DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease);
 };
 
@@ -136,9 +143,13 @@
 // initiates a nested message-loop when it receives a mouse-press event.
 class NestedLoopCaptureView : public View {
  public:
-  explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {}
+  explicit NestedLoopCaptureView(Widget* widget)
+      : run_loop_(base::RunLoop::Type::kNestableTasksAllowed),
+        widget_(widget) {}
   ~NestedLoopCaptureView() override {}
 
+  base::OnceClosure GetQuitClosure() { return run_loop_.QuitClosure(); }
+
  private:
   // View:
   bool OnMousePressed(const ui::MouseEvent& event) override {
@@ -147,10 +158,12 @@
     widget_->SetCapture(widget_->GetContentsView());
     EXPECT_TRUE(widget_->HasCapture());
 
-    base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).Run();
+    run_loop_.Run();
     return true;
   }
 
+  base::RunLoop run_loop_;
+
   Widget* widget_;
 
   DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView);
@@ -534,10 +547,10 @@
   Widget* first = CreateTopLevelFramelessPlatformWidget();
   Widget* second = CreateTopLevelFramelessPlatformWidget();
 
-  View* container = new NestedLoopCaptureView(second);
+  NestedLoopCaptureView* container = new NestedLoopCaptureView(second);
   first->SetContentsView(container);
 
-  second->SetContentsView(new ExitLoopOnRelease());
+  second->SetContentsView(new ExitLoopOnRelease(container->GetQuitClosure()));
 
   first->SetSize(gfx::Size(100, 100));
   first->Show();
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index 533f137..3c7604a 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -1403,7 +1403,7 @@
     base::RunLoop runloop;
     quit_closure_ = runloop.QuitClosure();
     runloop.Run();
-    quit_closure_ = base::Closure();
+    quit_closure_.Reset();
   }
 
   void OnWidgetClosing(Widget* widget) override { expect_paint_ = false; }
@@ -1415,7 +1415,7 @@
       received_paint_while_hidden_ = true;
     views::Widget::OnNativeWidgetPaint(context);
     if (!quit_closure_.is_null())
-      quit_closure_.Run();
+      std::move(quit_closure_).Run();
   }
 
   // WidgetObserver:
@@ -1427,7 +1427,7 @@
   bool received_paint_;
   bool expect_paint_;
   bool received_paint_while_hidden_;
-  base::Closure quit_closure_;
+  base::OnceClosure quit_closure_;
 
   DISALLOW_COPY_AND_ASSIGN(DesktopAuraTestValidPaintWidget);
 };
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 3703f16..3f52bcdc 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -1233,8 +1233,9 @@
   return ViewsDelegate::GetInstance()
              ? ViewsDelegate::GetInstance()->GetAppbarAutohideEdges(
                    monitor,
-                   base::Bind(&HWNDMessageHandler::OnAppbarAutohideEdgesChanged,
-                              autohide_factory_.GetWeakPtr()))
+                   base::BindOnce(
+                       &HWNDMessageHandler::OnAppbarAutohideEdgesChanged,
+                       autohide_factory_.GetWeakPtr()))
              : ViewsDelegate::EDGE_BOTTOM;
 }
 
diff --git a/ui/views_bridge_mac/bridged_native_widget_impl.h b/ui/views_bridge_mac/bridged_native_widget_impl.h
index 95399ae..5c370f1 100644
--- a/ui/views_bridge_mac/bridged_native_widget_impl.h
+++ b/ui/views_bridge_mac/bridged_native_widget_impl.h
@@ -187,6 +187,10 @@
   // Redispatch a keyboard event using the widget's window's CommandDispatcher.
   // Return true if the event is handled.
   bool RedispatchKeyEvent(NSEvent* event);
+  // Save an NSEvent to be used at the mojo version of RedispatchKeyEvent,
+  // rather than (inaccurately) reconstructing the NSEvent.
+  // https://crbug.com/942690
+  void SaveKeyEventForRedispatch(NSEvent* event);
 
   // display::DisplayObserver:
   void OnDisplayMetricsChanged(const display::Display& display,
@@ -297,6 +301,7 @@
   base::scoped_nsobject<ViewsNSWindowDelegate> window_delegate_;
   base::scoped_nsobject<NSObject<CommandDispatcherDelegate>>
       window_command_dispatcher_delegate_;
+  base::scoped_nsobject<NSEvent> saved_redispatch_event_;
 
   base::scoped_nsobject<BridgedContentView> bridged_view_;
   std::unique_ptr<ui::ScopedNSViewIdMapping> bridged_view_id_mapping_;
diff --git a/ui/views_bridge_mac/bridged_native_widget_impl.mm b/ui/views_bridge_mac/bridged_native_widget_impl.mm
index 29610c0..a5051786 100644
--- a/ui/views_bridge_mac/bridged_native_widget_impl.mm
+++ b/ui/views_bridge_mac/bridged_native_widget_impl.mm
@@ -7,6 +7,7 @@
 #import <objc/runtime.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <cmath>
 
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -1051,6 +1052,10 @@
   return [[window_ commandDispatcher] redispatchKeyEvent:event];
 }
 
+void BridgedNativeWidgetImpl::SaveKeyEventForRedispatch(NSEvent* event) {
+  saved_redispatch_event_.reset([event retain]);
+}
+
 NSWindow* BridgedNativeWidgetImpl::ns_window() {
   return window_.get();
 }
@@ -1208,6 +1213,23 @@
     const base::string16& characters,
     const base::string16& characters_ignoring_modifiers,
     uint32_t key_code) {
+  // If we saved an event for redispatch, and that event looks similar to the
+  // (potentially mangled) event parameters that we received, then use the saved
+  // event.
+  // https://crbug.com/942690
+  if (saved_redispatch_event_) {
+    // Consider two events to have the same timestamp if they are within 0.1 ms.
+    constexpr double kTimestampThreshold = 0.0001;
+    if ([saved_redispatch_event_ type] == type &&
+        base::SysNSStringToUTF16([saved_redispatch_event_ characters]) ==
+            characters &&
+        std::fabs([saved_redispatch_event_ timestamp] - timestamp) <
+            kTimestampThreshold) {
+      RedispatchKeyEvent(saved_redispatch_event_.autorelease());
+      return;
+    }
+    saved_redispatch_event_.reset();
+  }
   NSEvent* event =
       [NSEvent keyEventWithType:static_cast<NSEventType>(type)
                              location:NSZeroPoint
diff --git a/ui/views_content_client/views_content_client.cc b/ui/views_content_client/views_content_client.cc
index a1dcb4e..95e750e 100644
--- a/ui/views_content_client/views_content_client.cc
+++ b/ui/views_content_client/views_content_client.cc
@@ -4,6 +4,8 @@
 
 #include "ui/views_content_client/views_content_client.h"
 
+#include <utility>
+
 #include "build/build_config.h"
 #include "content/public/app/content_main.h"
 #include "ui/views_content_client/views_content_main_delegate.h"
@@ -39,4 +41,11 @@
   return content::ContentMain(params);
 }
 
+void ViewsContentClient::OnPreMainMessageLoopRun(
+    content::BrowserContext* browser_context,
+    gfx::NativeWindow window_context) {
+  std::move(on_pre_main_message_loop_run_callback_)
+      .Run(browser_context, window_context);
+}
+
 }  // namespace ui
diff --git a/ui/views_content_client/views_content_client.h b/ui/views_content_client/views_content_client.h
index d71a12fb..e5799f23 100644
--- a/ui/views_content_client/views_content_client.h
+++ b/ui/views_content_client/views_content_client.h
@@ -45,9 +45,9 @@
 // }
 class VIEWS_CONTENT_CLIENT_EXPORT ViewsContentClient {
  public:
-  typedef base::Callback<
-      void(content::BrowserContext* browser_context,
-           gfx::NativeWindow window_context)> Task;
+  using OnPreMainMessageLoopRunCallback =
+      base::OnceCallback<void(content::BrowserContext* browser_context,
+                              gfx::NativeWindow window_context)>;
 
 #if defined(OS_WIN)
   ViewsContentClient(HINSTANCE instance,
@@ -63,8 +63,16 @@
 
   // The task to run at the end of BrowserMainParts::PreMainMessageLoopRun().
   // Ignored if this is not the main process.
-  void set_task(const Task& task) { task_ = task; }
-  const Task& task() const { return task_; }
+  void set_on_pre_main_message_loop_run_callback(
+      OnPreMainMessageLoopRunCallback callback) {
+    on_pre_main_message_loop_run_callback_ = std::move(callback);
+  }
+
+  // Calls the OnPreMainMessageLoopRun callback. |browser_context| is the
+  // current browser context. |window_context| is a candidate root window that
+  // may be null.
+  void OnPreMainMessageLoopRun(content::BrowserContext* browser_context,
+                               gfx::NativeWindow window_context);
 
   // Called by ViewsContentClientMainParts to supply the quit-closure to use
   // to exit RunMain().
@@ -81,7 +89,7 @@
   int argc_;
   const char** argv_;
 #endif
-  Task task_;
+  OnPreMainMessageLoopRunCallback on_pre_main_message_loop_run_callback_;
   base::OnceClosure quit_closure_;
 
   DISALLOW_COPY_AND_ASSIGN(ViewsContentClient);
diff --git a/ui/views_content_client/views_content_client_main_parts_chromeos.cc b/ui/views_content_client/views_content_client_main_parts_chromeos.cc
index e2618ec..a196550 100644
--- a/ui/views_content_client/views_content_client_main_parts_chromeos.cc
+++ b/ui/views_content_client/views_content_client_main_parts_chromeos.cc
@@ -64,7 +64,8 @@
 
   // Ensure Aura knows where to open new windows.
   aura::Window* root_window = wm_test_helper_->host()->window();
-  views_content_client()->task().Run(browser_context(), root_window);
+  views_content_client()->OnPreMainMessageLoopRun(browser_context(),
+                                                  root_window);
 }
 
 void ViewsContentClientMainPartsChromeOS::PostMainMessageLoopRun() {
diff --git a/ui/views_content_client/views_content_client_main_parts_desktop_aura.cc b/ui/views_content_client/views_content_client_main_parts_desktop_aura.cc
index 0f47013..adbfbd46 100644
--- a/ui/views_content_client/views_content_client_main_parts_desktop_aura.cc
+++ b/ui/views_content_client/views_content_client_main_parts_desktop_aura.cc
@@ -39,7 +39,7 @@
 
   display::Screen::SetScreenInstance(views::CreateDesktopScreen());
 
-  views_content_client()->task().Run(browser_context(), NULL);
+  views_content_client()->OnPreMainMessageLoopRun(browser_context(), nullptr);
 }
 
 }  // namespace
diff --git a/ui/views_content_client/views_content_client_main_parts_mac.mm b/ui/views_content_client/views_content_client_main_parts_mac.mm
index de098d1..0715f73b 100644
--- a/ui/views_content_client/views_content_client_main_parts_mac.mm
+++ b/ui/views_content_client/views_content_client_main_parts_mac.mm
@@ -4,7 +4,10 @@
 
 #import <Cocoa/Cocoa.h>
 
+#include <utility>
+
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
@@ -20,11 +23,11 @@
 // activate a task when the application has finished loading.
 @interface ViewsContentClientAppController : NSObject<NSApplicationDelegate> {
  @private
-  base::Closure task_;
+  base::OnceClosure onApplicationDidFinishLaunching_;
 }
 
 // Set the task to run after receiving -applicationDidFinishLaunching:.
-- (void)setTask:(const base::Closure&)task;
+- (void)setOnApplicationDidFinishLaunching:(base::OnceClosure)task;
 
 @end
 
@@ -67,9 +70,12 @@
   // the widget can activate, but (even if configured) the mainMenu won't be
   // ready to switch over in the OSX UI, so it will look strange.
   NSWindow* window_context = nil;
-  [app_controller_ setTask:base::Bind(views_content_client()->task(),
-                                      base::Unretained(browser_context()),
-                                      base::Unretained(window_context))];
+  [app_controller_
+      setOnApplicationDidFinishLaunching:
+          base::BindOnce(&ViewsContentClient::OnPreMainMessageLoopRun,
+                         base::Unretained(views_content_client()),
+                         base::Unretained(browser_context()),
+                         base::Unretained(window_context))];
 }
 
 ViewsContentClientMainPartsMac::~ViewsContentClientMainPartsMac() {
@@ -99,8 +105,8 @@
 
 @implementation ViewsContentClientAppController
 
-- (void)setTask:(const base::Closure&)task {
-  task_ = task;
+- (void)setOnApplicationDidFinishLaunching:(base::OnceClosure)task {
+  onApplicationDidFinishLaunching_ = std::move(task);
 }
 
 - (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
@@ -127,7 +133,7 @@
 
   CHECK([NSApp isKindOfClass:[ShellCrApplication class]]);
 
-  task_.Run();
+  std::move(onApplicationDidFinishLaunching_).Run();
 }
 
 @end