diff --git a/BUILD.gn b/BUILD.gn
index 607e439..b722743 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -171,7 +171,7 @@
       "//net:net_perftests",
       "//third_party/angle/src/tests:angle_end2end_tests",
       "//third_party/angle/src/tests:angle_unittests",
-      "//third_party/blink/renderer/controller:webkit_unit_tests",
+      "//third_party/blink/renderer/controller:blink_unittests",
       "//third_party/blink/renderer/platform/wtf:wtf_unittests",
       "//ui/gl:gl_unittests",
       "//url/ipc:url_ipc_unittests",
@@ -920,17 +920,17 @@
     testonly = true
 
     deps = [
-      ":webkit_layout_tests",
+      ":blink_web_tests",
       "//third_party/blink/public:all_blink",
     ]
   }
 
-  # Layout tests runner
+  # Web tests runner
   # third_party/blink/tools/run_web_tests.py
-  group("run_webkit_tests") {
+  group("run_web_tests") {
     testonly = true
     deps = [
-      ":webkit_layout_tests",
+      ":blink_web_tests",
     ]
   }
 
@@ -960,21 +960,21 @@
   # The _exparchive at the end of the name indicates to the isolate recipe
   # that the isolate should be archived separately using the `exparchive`
   # command, rather than as part of the normal `batcharchive` command.
-  group("webkit_layout_tests_exparchive") {
+  group("blink_web_tests_exparchive") {
     testonly = true
     deps = [
-      ":webkit_layout_tests",
+      ":blink_web_tests",
     ]
     data_deps = [
-      ":webkit_layout_tests",
+      ":blink_web_tests",
     ]
   }
 
-  # This target contains only a small subset of the layout tests,
+  # This target contains only a small subset of the web tests,
   # and is useful for testing with the regular isolate mechanism.
-  # To run the full layout test suite you need to use
-  # :webkit_layout_tests_exparchive, above, instead.
-  generated_script_test("webkit_layout_tests") {
+  # To run the full web test suite you need to use
+  # :blink_web_tests_exparchive, above, instead.
+  generated_script_test("blink_web_tests") {
     generator_script =
         "//testing/scripts/generators/gen_run_web_tests_script.py"
     extra_args = []
@@ -1101,7 +1101,7 @@
     ]
   }
 
-  group("webkit_python_tests") {
+  group("blink_python_tests") {
     data = [
       "//build/android/",
       "//components/crash/content/tools/generate_breakpad_symbols.py",
diff --git a/DEPS b/DEPS
index 8df51b5340..c1a1885 100644
--- a/DEPS
+++ b/DEPS
@@ -126,11 +126,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': '5105d68f93e9c294d9cfa56247ca2119ec8da8d4',
+  'skia_revision': '6152470dc69ee96172e5f4c3270f98e47ff9693d',
   # 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': '9c2fa40d2db3eb2f7e2f15196c918e4dfd2b8710',
+  'v8_revision': 'd34a141dae9be2c1f66509c7c449992ea34d058a',
   # 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.
@@ -138,7 +138,7 @@
   # 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': '3805122b0297b8208ff734a3bcdaa12850b60a00',
+  'angle_revision': '52047de4d41f6861e35b7073dd180edffd64c540',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -146,7 +146,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'bf42afa0df142896bea074f0d4d1d4fd6d3309ab',
+  'pdfium_revision': 'e6fcdfa5bb6e1fca1bb833cbace54b754ca395a6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -189,7 +189,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': '63f9b85be556ed82b32e4b9f30b266152d523cdc',
+  'catapult_revision': '8ba5518f87cc9179d81af3df7eaad5fb15a34acf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -253,7 +253,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '1809ff74233de497ad2b14a06eaddde54ecba7fd',
+  'dawn_revision': 'b2207737abd32e20e39a1ac00987179626f050c7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -732,7 +732,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'e67fcf9ed6c6bff5ea5a202cd278601f95f9a7c1',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '499f9dd2f00ec1c72fd7f2322d756db28e17eb9d',
       'condition': 'checkout_linux',
   },
 
@@ -1092,7 +1092,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '635129336c5010e0a6324fde2ba680ca7404c5e9',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '3271875cb274dbfb0ada8d2cec976bf4e1e7d0ca',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1255,7 +1255,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a0f51b2e123f39c9ff12e621b0b47dd28dd64424',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'f13c2cd9ee41f4ca572232a4e397b05449474632',
+    Var('webrtc_git') + '/src.git' + '@' + 'b1ea48c2e8682e9c2f67ff3cfa4e9dbaebbb8cc7',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1296,7 +1296,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7ec7019349934f9b0cee81701c180fac9652282f',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@116ba2e6c585afa1507e806e162cae6c098bf9ef',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/system/network/vpn_list_view.cc b/ash/system/network/vpn_list_view.cc
index 2ac950b..a2c4813 100644
--- a/ash/system/network/vpn_list_view.cc
+++ b/ash/system/network/vpn_list_view.cc
@@ -324,6 +324,10 @@
   list_empty_ = false;
 }
 
+void VPNListView::AddProviderAndNetworks(const VPNProvider& vpn_provider) {
+  AddProviderAndNetworks(vpn_provider, NetworkStateHandler::NetworkStateList());
+}
+
 void VPNListView::AddProviderAndNetworks(
     const VPNProvider& vpn_provider,
     const NetworkStateHandler::NetworkStateList& networks) {
@@ -351,6 +355,21 @@
   }
 }
 
+bool VPNListView::ProcessProviderForNetwork(
+    const NetworkState* network,
+    const NetworkStateHandler::NetworkStateList& networks,
+    std::vector<VPNProvider>* providers) {
+  for (auto provider_iter = providers->begin();
+       provider_iter != providers->end(); ++provider_iter) {
+    if (!VpnProviderMatchesNetwork(*provider_iter, *network))
+      continue;
+    AddProviderAndNetworks(*provider_iter, networks);
+    providers->erase(provider_iter);
+    return true;
+  }
+  return false;
+}
+
 void VPNListView::AddProvidersAndNetworks(
     const NetworkStateHandler::NetworkStateList& networks) {
   // Get the list of VPN providers enabled in the primary user's profile.
@@ -364,56 +383,33 @@
 
   // Add connected ARCVPN network. If we can find the correct provider, nest
   // the network under the provider. Otherwise list it unnested.
-  for (const NetworkState* const& network : networks) {
+  for (const NetworkState* network : networks) {
     if (!network->IsConnectingOrConnected())
       break;
     if (network->GetVpnProviderType() != shill::kProviderArcVpn)
       continue;
 
-    bool found_provider = false;
-    for (auto arc_provider_iter = arc_providers.begin();
-         arc_provider_iter != arc_providers.end(); ++arc_provider_iter) {
-      if (!VpnProviderMatchesNetwork(*arc_provider_iter, *network))
-        continue;
-      AddProviderAndNetworks(*arc_provider_iter, networks);
-      arc_providers.erase(arc_provider_iter);
-      found_provider = true;
-      break;
-    }
-    // No matched provider found for this network. Show it unnested.
+    // If no matched provider found for this network. Show it unnested.
     // TODO(lgcheng@) add UMA status to track this.
-    if (!found_provider)
+    if (!ProcessProviderForNetwork(network, networks, &arc_providers))
       AddNetwork(network);
   }
 
   // Add providers with at least one configured network along with their
   // networks. Providers are added in the order of their highest priority
   // network.
-  for (const NetworkState* const& network : networks) {
-    for (auto extension_provider_iter = extension_providers.begin();
-         extension_provider_iter != extension_providers.end();
-         ++extension_provider_iter) {
-      if (!VpnProviderMatchesNetwork(*extension_provider_iter, *network))
-        continue;
-      AddProviderAndNetworks(*extension_provider_iter, networks);
-      extension_providers.erase(extension_provider_iter);
-      break;
-    }
-  }
-
-  // Create a local networkstate list. Help AddProviderAndNetworks() by passing
-  // empty list of network states.
-  NetworkStateHandler::NetworkStateList networkstate_empty_list;
+  for (const NetworkState* network : networks)
+    ProcessProviderForNetwork(network, networks, &extension_providers);
 
   // Add providers without any configured networks, in the order that the
   // providers were returned by the extensions system.
-  for (const VPNProvider& provider : extension_providers)
-    AddProviderAndNetworks(provider, networkstate_empty_list);
+  for (const VPNProvider& extension_provider : extension_providers)
+    AddProviderAndNetworks(extension_provider);
 
   // Add Arc VPN providers without any connected or connecting networks. These
   // providers are sorted by last launch time.
   for (const VPNProvider& arc_provider : arc_providers) {
-    AddProviderAndNetworks(arc_provider, networkstate_empty_list);
+    AddProviderAndNetworks(arc_provider);
   }
 }
 
diff --git a/ash/system/network/vpn_list_view.h b/ash/system/network/vpn_list_view.h
index 6e40f73..676dd36c 100644
--- a/ash/system/network/vpn_list_view.h
+++ b/ash/system/network/vpn_list_view.h
@@ -59,11 +59,25 @@
   void AddNetwork(const chromeos::NetworkState* network);
 
   // Adds the VPN provider identified by |vpn_provider| to the list, along with
+  // no networks that belong to this provider.
+  void AddProviderAndNetworks(const VPNProvider& vpn_provider);
+
+  // Adds the VPN provider identified by |vpn_provider| to the list, along with
   // any networks that belong to this provider.
   void AddProviderAndNetworks(
       const VPNProvider& vpn_provider,
       const chromeos::NetworkStateHandler::NetworkStateList& networks);
 
+  // Finds VPN provider from |providers| that matches given |network|. Then adds
+  // the VPN provider along with any networks that belong to this provider. Will
+  // also remove the match from |providers| to avoid showing duplicate provider
+  // entry in VPN list view.
+  // Returns true if finds a match, returns false otherwise.
+  bool ProcessProviderForNetwork(
+      const chromeos::NetworkState* network,
+      const chromeos::NetworkStateHandler::NetworkStateList& networks,
+      std::vector<VPNProvider>* providers);
+
   // Adds all available VPN providers and networks to the list.
   void AddProvidersAndNetworks(
       const chromeos::NetworkStateHandler::NetworkStateList& networks);
diff --git a/base/memory/weak_ptr.cc b/base/memory/weak_ptr.cc
index c993fcb..64fd499 100644
--- a/base/memory/weak_ptr.cc
+++ b/base/memory/weak_ptr.cc
@@ -34,6 +34,10 @@
   return !invalidated_.IsSet();
 }
 
+void WeakReference::Flag::DetachFromSequence() {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
 WeakReference::Flag::~Flag() = default;
 
 WeakReference::WeakReference() = default;
@@ -54,25 +58,24 @@
   return flag_ && flag_->MaybeValid();
 }
 
-WeakReferenceOwner::WeakReferenceOwner() = default;
+WeakReferenceOwner::WeakReferenceOwner()
+    : flag_(MakeRefCounted<WeakReference::Flag>()) {}
 
 WeakReferenceOwner::~WeakReferenceOwner() {
-  Invalidate();
+  flag_->Invalidate();
 }
 
 WeakReference WeakReferenceOwner::GetRef() const {
-  // If we hold the last reference to the Flag then create a new one.
+  // If we hold the last reference to the Flag then detach the SequenceChecker.
   if (!HasRefs())
-    flag_ = new WeakReference::Flag();
+    flag_->DetachFromSequence();
 
   return WeakReference(flag_);
 }
 
 void WeakReferenceOwner::Invalidate() {
-  if (flag_) {
-    flag_->Invalidate();
-    flag_ = nullptr;
-  }
+  flag_->Invalidate();
+  flag_ = MakeRefCounted<WeakReference::Flag>();
 }
 
 WeakPtrBase::WeakPtrBase() : ptr_(0) {}
diff --git a/base/memory/weak_ptr.h b/base/memory/weak_ptr.h
index 6b94b0e..af2bd38 100644
--- a/base/memory/weak_ptr.h
+++ b/base/memory/weak_ptr.h
@@ -102,6 +102,8 @@
 
     bool MaybeValid() const;
 
+    void DetachFromSequence();
+
    private:
     friend class base::RefCountedThreadSafe<Flag>;
 
@@ -134,7 +136,7 @@
 
   WeakReference GetRef() const;
 
-  bool HasRefs() const { return flag_ && !flag_->HasOneRef(); }
+  bool HasRefs() const { return !flag_->HasOneRef(); }
 
   void Invalidate();
 
@@ -223,7 +225,6 @@
 class WeakPtr : public internal::WeakPtrBase {
  public:
   WeakPtr() = default;
-
   WeakPtr(std::nullptr_t) {}
 
   // Allow conversion from U to T provided U "is a" T. Note that this
diff --git a/build/android/pylib/gtest/gtest_config.py b/build/android/pylib/gtest/gtest_config.py
index 332a31d..3ac1955 100644
--- a/build/android/pylib/gtest/gtest_config.py
+++ b/build/android/pylib/gtest/gtest_config.py
@@ -21,6 +21,7 @@
 STABLE_TEST_SUITES = [
     'android_webview_unittests',
     'base_unittests',
+    'blink_unittests',
     'breakpad_unittests',
     'cc_unittests',
     'components_unittests',
@@ -42,7 +43,6 @@
     'ui_base_unittests',
     'ui_touch_selection_unittests',
     'unit_tests_apk',
-    'webkit_unit_tests',
 ]
 
 # Tests fail in component=shared_library build, which is required for ASan.
diff --git a/build/android/resource_sizes.py b/build/android/resource_sizes.py
index c986962..301acf3 100755
--- a/build/android/resource_sizes.py
+++ b/build/android/resource_sizes.py
@@ -617,14 +617,11 @@
 
 
 def _Analyze(apk_path, chartjson, args):
-  metric_prefix = os.path.basename(args.input) + '_'
-  metric_prefix = metric_prefix.replace('.minimal.apks', '.apk')
 
-  def report_func(title, *args):
+  def report_func(*args):
     # Do not add any new metrics without also documenting them in:
     # //docs/speed/binary_size/metrics.md.
-    title = metric_prefix + title
-    perf_tests_results_helper.ReportPerfResult(chartjson, title, *args)
+    perf_tests_results_helper.ReportPerfResult(chartjson, *args)
 
   out_dir, tool_prefix = _ConfigOutDirAndToolsPrefix(args.out_dir)
   is_bundle = args.input.endswith('.apks')
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 21a83da..7f9d51d 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1120,7 +1120,7 @@
           # aggregates enabling subsequent optimizations) leads to
           # invalid code generation when using the Android NDK's
           # compiler (r5-r7). This can be verified using
-          # webkit_unit_tests' WTF.Checked_int8_t test.
+          # blink_unittests' WTF.Checked_int8_t test.
           "-fno-tree-sra",
 
           # The following option is disabled to improve binary
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 999f15b..a879d52 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-1c91c8faa342f73db94a3973fd9759497f1f3c27
\ No newline at end of file
+4236f65568daabc3872ae42c603099873e984bec
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 85bd396..3d14acc 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-f42d146adf49641d73ba7e4a8901578fa2114663
\ No newline at end of file
+2f6532e63eeaa8e3bed08ce65d551310a075fd9d
\ No newline at end of file
diff --git a/cc/layers/video_layer_impl.cc b/cc/layers/video_layer_impl.cc
index 84c9913b..eab1e6a 100644
--- a/cc/layers/video_layer_impl.cc
+++ b/cc/layers/video_layer_impl.cc
@@ -104,8 +104,11 @@
 
   if (!updater_) {
     const LayerTreeSettings& settings = layer_tree_impl()->settings();
+    // TODO(sergeyu): Pass RasterContextProvider when it's available. Then
+    // remove ContextProvider parameter from VideoResourceUpdater.
     updater_ = std::make_unique<media::VideoResourceUpdater>(
         layer_tree_impl()->context_provider(),
+        /*raster_context_provider=*/nullptr,
         layer_tree_impl()->layer_tree_frame_sink(),
         layer_tree_impl()->resource_provider(),
         settings.use_stream_video_draw_quad,
diff --git a/cc/resources/resource_pool_unittest.cc b/cc/resources/resource_pool_unittest.cc
index 5f15954..370b7750 100644
--- a/cc/resources/resource_pool_unittest.cc
+++ b/cc/resources/resource_pool_unittest.cc
@@ -244,8 +244,9 @@
 
   std::vector<viz::ResourceId> export_ids = {resource.resource_id_for_export()};
   std::vector<viz::TransferableResource> transferable_resources;
-  resource_provider_->PrepareSendToParent(export_ids, &transferable_resources,
-                                          context_provider_.get());
+  resource_provider_->PrepareSendToParent(
+      export_ids, &transferable_resources,
+      static_cast<viz::RasterContextProvider*>(context_provider_.get()));
   auto returned_resources =
       viz::TransferableResource::ReturnResources(transferable_resources);
   ASSERT_EQ(1u, returned_resources.size());
@@ -276,8 +277,9 @@
   EXPECT_TRUE(resource_pool_->PrepareForExport(resource));
 
   std::vector<viz::TransferableResource> transfers;
-  resource_provider_->PrepareSendToParent({resource.resource_id_for_export()},
-                                          &transfers, context_provider_.get());
+  resource_provider_->PrepareSendToParent(
+      {resource.resource_id_for_export()}, &transfers,
+      static_cast<viz::RasterContextProvider*>(context_provider_.get()));
 
   resource_pool_->ReleaseResource(std::move(resource));
   EXPECT_EQ(40000u, resource_pool_->GetTotalMemoryUsageForTesting());
@@ -316,8 +318,9 @@
   SetBackingOnResource(resource);
   EXPECT_TRUE(resource_pool_->PrepareForExport(resource));
   std::vector<viz::TransferableResource> transfers;
-  resource_provider_->PrepareSendToParent({resource.resource_id_for_export()},
-                                          &transfers, context_provider_.get());
+  resource_provider_->PrepareSendToParent(
+      {resource.resource_id_for_export()}, &transfers,
+      static_cast<viz::RasterContextProvider*>(context_provider_.get()));
 
   resource_pool_->ReleaseResource(std::move(resource));
   EXPECT_EQ(40000u, resource_pool_->GetTotalMemoryUsageForTesting());
@@ -534,8 +537,9 @@
   // Export the resource to the display compositor, so it will be busy once
   // released.
   std::vector<viz::TransferableResource> transfers;
-  resource_provider_->PrepareSendToParent({resource.resource_id_for_export()},
-                                          &transfers, context_provider_.get());
+  resource_provider_->PrepareSendToParent(
+      {resource.resource_id_for_export()}, &transfers,
+      static_cast<viz::RasterContextProvider*>(context_provider_.get()));
 
   // Release the resource making it busy.
   resource_pool_->ReleaseResource(std::move(resource));
@@ -603,7 +607,7 @@
   std::vector<viz::TransferableResource> transfers;
   resource_provider_->PrepareSendToParent(
       {busy_resource.resource_id_for_export()}, &transfers,
-      context_provider_.get());
+      static_cast<viz::RasterContextProvider*>(context_provider_.get()));
 
   // Release the resource making it busy.
   resource_pool_->ReleaseResource(std::move(busy_resource));
@@ -695,8 +699,9 @@
   EXPECT_TRUE(resource_pool_->PrepareForExport(resource));
 
   std::vector<viz::TransferableResource> transfer;
-  resource_provider_->PrepareSendToParent({resource.resource_id_for_export()},
-                                          &transfer, context_provider_.get());
+  resource_provider_->PrepareSendToParent(
+      {resource.resource_id_for_export()}, &transfer,
+      static_cast<viz::RasterContextProvider*>(context_provider_.get()));
 
   // The verified_flush flag will be set by the ResourceProvider when it exports
   // the resource.
diff --git a/chrome/OWNERS b/chrome/OWNERS
index 7672069..f656e8d 100644
--- a/chrome/OWNERS
+++ b/chrome/OWNERS
@@ -1,11 +1,17 @@
 # This OWNERS list is a last resort. Please prefer to use more specific OWNERS
 # where possible.
 
-# Reviewers:
+# Reviewers (in CET):
+droger@chromium.org
+treib@chromium.org
+vasilii@chromium.org
+
+# Reviewers (in EST):
 avi@chromium.org
-jochen@chromium.org
-sky@chromium.org
 thakis@chromium.org
+
+# Reviewers (in PST):
+sky@chromium.org
 thestig@chromium.org
 
 # per-file rules:
diff --git a/chrome/VERSION b/chrome/VERSION
index 90773828..a3beede 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=74
 MINOR=0
-BUILD=3705
+BUILD=3706
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncher.java
index aaafb8e..0356ec6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncher.java
@@ -13,9 +13,11 @@
 import android.support.annotation.Nullable;
 
 import org.chromium.base.StrictModeContext;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.base.task.AsyncTask;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeVersionInfo;
 import org.chromium.chrome.browser.IntentHandler;
@@ -89,7 +91,7 @@
                         ChromeFeatureList.ALLOW_NEW_INCOGNITO_TAB_INTENTS)
                 && PrefServiceBridge.getInstance().isIncognitoModeEnabled();
 
-        AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> setComponentEnabled(context, enable));
+        PostTask.postTask(TaskTraits.USER_VISIBLE, () -> setComponentEnabled(context, enable));
     }
 
     /**
@@ -98,6 +100,8 @@
      */
     @VisibleForTesting
     static void setComponentEnabled(Context context, boolean enabled) {
+        ThreadUtils.assertOnBackgroundThread();
+
         PackageManager packageManager = context.getPackageManager();
         ComponentName componentName = new ComponentName(context, IncognitoTabLauncher.class);
 
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 6dc36e3..db8e4de7 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-74.0.3704.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-74.0.3705.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index d695899..dba863f 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7106,9 +7106,6 @@
         <message name="IDS_SYNC_SETTINGS_NOT_CONFIRMED" desc="The message that appears in the settings page indicating that user is signed in, but sync hasn't started because the sync settings haven't been confirmed.">
           Confirm sync settings to start sync.
         </message>
-        <message name="IDS_SYNC_AUTHENTICATING_LABEL" desc="Label to display while the user is being authenticated to use sync.">
-          Authenticating...
-        </message>
         <message name="IDS_SYNC_ERROR_SIGNING_IN" desc="An error was encountered while signing the user in.">
           Error signing in.
         </message>
@@ -9332,6 +9329,33 @@
       <message name="IDS_WEBAUTHN_CABLE_ACTIVATE_DESCRIPTION" desc="Contents of the dialog shown when the user tries to sign-in using a phone as a security key.">
         A notification was sent to your phone. Follow the prompts to confirm it's you.
       </message>
+      <message name="IDS_WEBAUTHN_PIN_ENTRY_TITLE" desc="Title of the dialog shown when instructing the user to enter the PIN code to use a security key (an external physical device for user authentication) with their computer.">
+        PIN required
+      </message>
+      <message name="IDS_WEBAUTHN_PIN_ENTRY_DESCRIPTION" desc="Description in the dialog shown when instructing the user to enter the PIN code to use a security key (an external physical device for user authentication) with their computer.">
+        Enter the PIN for your security key
+      </message>
+      <message name="IDS_WEBAUTHN_PIN_ENTRY_PIN_LABEL" desc="Label text. Displayed next to a text box where the user enters the PIN code for their security key (an external physical device for user authentication).">
+        PIN
+      </message>
+      <message name="IDS_WEBAUTHN_PIN_ENTRY_NEXT" desc="Button text. The user clicks this button once they have entered the PIN code into a text box in order to continue.">
+        Next
+      </message>
+      <message name="IDS_WEBAUTHN_PIN_SETUP_DESCRIPTION" desc="Description in the dialog shown when instructing the user to set up a PIN code to use with their security key (an external physical device for user authentication) with their computer.">
+        Set up a new PIN for your security key
+      </message>
+      <message name="IDS_WEBAUTHN_PIN_SETUP_CONFIRMATION_LABEL" desc="Label text. Displayed next to a text box where the user re-enters the PIN code for their security key (an external physical device for user authentication).">
+        Confirm PIN
+      </message>
+      <message name="IDS_WEBAUTHN_PIN_ENTRY_ERROR_INVALID_CHARACTERS" 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 contains invalid characters.">
+        PIN contains invalid characters
+      </message>
+      <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_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>
    </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.">
@@ -9343,10 +9367,10 @@
     <message name="IDS_INCOGNITO_WINDOW_COUNTER_TITLE" desc="The title of the incognito window counter dialog.">
       Incognito
     </message>
-    <message name="IDS_INCOGNITO_WINDOW_COUNTER_MESSAGE" desc="The message showing the number of open incogntio windows. [ICU Syntax]">
+    <message name="IDS_INCOGNITO_WINDOW_COUNTER_MESSAGE" desc="The message showing the number of open incogntio windows. This is not used for zero windows.[ICU Syntax]">
       {0, plural,
-        =1 {}
-        other {# windows are open}
+        =1 {# open window}
+        other {# open windows}
       }
     </message>
     <message name="IDS_INCOGNITO_WINDOW_COUNTER_CLOSE_BUTTON" desc="The text of the button offering to close all incognito windows.">
diff --git a/chrome/app/onboarding_welcome_strings.grdp b/chrome/app/onboarding_welcome_strings.grdp
index fa158c0..adee72d 100644
--- a/chrome/app/onboarding_welcome_strings.grdp
+++ b/chrome/app/onboarding_welcome_strings.grdp
@@ -61,14 +61,14 @@
   <message name="IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_ART_TITLE" desc="Label for choosing a background/wallpaper from the 'Art' category">
     Art
   </message>
-  <message name="IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_LANDSCAPE_TITLE" desc="Label for choosing a background/wallpaper from the 'Landscape' category, as in a category of photos of nature">
+  <message name="IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_LANDSCAPE_TITLE" desc="Label for choosing a background/wallpaper from the 'Landscape' category, as in a category of photos of scenery and large outdoor spaces.">
     Landscape
   </message>
   <message name="IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_CITYSCAPE_TITLE" desc="Label for choosing a background/wallpaper from the 'Cityscape' category">
     Cityscape
   </message>
-  <message name="IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_SEASCAPE_TITLE" desc="Label for choosing a background/wallpaper from the 'Seascape' category">
-    Seascape
+  <message name="IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_LIFE_TITLE" desc="Label for choosing a background/wallpaper from the 'Life' category, as in a category of photos of nature, flowers, and buildings.">
+    Life
   </message>
   <message name="IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_GEOMETRIC_SHAPES_TITLE" desc="Label for choosing a background/wallpaper from the 'Geometric Shapes' category">
     Geometric shapes
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 2261758..6da9663 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -306,6 +306,7 @@
         <structure type="chrome_scaled_image" name="IDR_WEBAUTHN_ILLUSTRATION_PHONE" file="common/webauthn/phone.png" />
         <structure type="chrome_scaled_image" name="IDR_WEBAUTHN_ILLUSTRATION_USB" file="common/webauthn/usb.png" />
         <structure type="chrome_scaled_image" name="IDR_WEBAUTHN_ILLUSTRATION_WELCOME" file="common/webauthn/welcome.png" />
+        <structure type="chrome_scaled_image" name="IDR_WEBAUTHN_ILLUSTRATION_PIN" file="common/webauthn/usb.png" />
       </if>
       <if expr="is_macosx">
         <structure type="chrome_scaled_image" name="IDR_WEBAUTHN_ILLUSTRATION_TOUCHID" file="common/webauthn/touchid.png" />
diff --git a/chrome/browser/apps/app_service/app_icon_factory.cc b/chrome/browser/apps/app_service/app_icon_factory.cc
index 3b4769a6..15b26863 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.cc
+++ b/chrome/browser/apps/app_service/app_icon_factory.cc
@@ -49,6 +49,7 @@
 // Runs |callback| passing an IconValuePtr with a compressed image: a
 // std::vector<uint8_t>.
 void RunCallbackWithCompressedData(
+    bool is_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback,
     std::vector<uint8_t> data) {
   apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
@@ -56,12 +57,14 @@
                              ? apps::mojom::IconCompression::kUnknown
                              : apps::mojom::IconCompression::kCompressed;
   iv->compressed = std::move(data);
+  iv->is_placeholder_icon = is_placeholder_icon;
   std::move(callback).Run(std::move(iv));
 }
 
 // Like RunCallbackWithCompressedData, but calls "fallback(callback)" if the
 // data is empty.
 void RunCallbackWithCompressedDataWithFallback(
+    bool is_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback,
     base::OnceCallback<void(apps::mojom::Publisher::LoadIconCallback)> fallback,
     std::vector<uint8_t> data) {
@@ -69,17 +72,20 @@
     std::move(fallback).Run(std::move(callback));
     return;
   }
-  RunCallbackWithCompressedData(std::move(callback), std::move(data));
+  RunCallbackWithCompressedData(is_placeholder_icon, std::move(callback),
+                                std::move(data));
 }
 
 // Runs |callback| passing an IconValuePtr with an uncompressed image: a
 // SkBitmap.
 void RunCallbackWithUncompressedSkBitmap(
+    bool is_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback,
     const SkBitmap& bitmap) {
   apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
   iv->icon_compression = apps::mojom::IconCompression::kUncompressed;
   iv->uncompressed = gfx::ImageSkia(gfx::ImageSkiaRep(bitmap, 0.0f));
+  iv->is_placeholder_icon = is_placeholder_icon;
   std::move(callback).Run(std::move(iv));
 }
 
@@ -87,6 +93,7 @@
 // std::vector<uint8_t> to a SkBitmap. It calls "fallback(callback)" if the
 // data is empty.
 void RunCallbackWithCompressedDataToUncompressWithFallback(
+    bool is_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback,
     base::OnceCallback<void(apps::mojom::Publisher::LoadIconCallback)> fallback,
     std::vector<uint8_t> data) {
@@ -98,18 +105,20 @@
       content::ServiceManagerConnection::GetForProcess()->GetConnector(), data,
       data_decoder::mojom::ImageCodec::DEFAULT, false,
       data_decoder::kDefaultMaxSizeInBytes, gfx::Size(),
-      base::BindOnce(&RunCallbackWithUncompressedSkBitmap,
+      base::BindOnce(&RunCallbackWithUncompressedSkBitmap, is_placeholder_icon,
                      std::move(callback)));
 }
 
 // Runs |callback| passing an IconValuePtr with an uncompressed image: an
 // ImageSkia.
 void RunCallbackWithUncompressedImageSkia(
+    bool is_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback,
     const gfx::ImageSkia image) {
   apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
   iv->icon_compression = apps::mojom::IconCompression::kUncompressed;
   iv->uncompressed = image;
+  iv->is_placeholder_icon = is_placeholder_icon;
   std::move(callback).Run(std::move(iv));
 }
 
@@ -117,11 +126,13 @@
 // an Image.
 void RunCallbackWithUncompressedImage(
     base::OnceCallback<void(gfx::ImageSkia*)> image_filter,
+    bool is_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback,
     const gfx::Image& image) {
   gfx::ImageSkia image_skia = image.AsImageSkia();
   std::move(image_filter).Run(&image_skia);
-  RunCallbackWithUncompressedImageSkia(std::move(callback), image_skia);
+  RunCallbackWithUncompressedImageSkia(is_placeholder_icon, std::move(callback),
+                                       image_skia);
 }
 
 // Forwards to extensions::ChromeAppIcon::ApplyEffects, with subtle differences
@@ -148,6 +159,7 @@
                            content::BrowserContext* context,
                            const std::string& extension_id,
                            apps::mojom::Publisher::LoadIconCallback callback) {
+  constexpr bool is_placeholder_icon = false;
   int size_hint_in_px = apps_util::ConvertDipToPx(size_hint_in_dip);
 
   const extensions::Extension* extension =
@@ -183,7 +195,7 @@
                     resize_function, apply_chrome_badge,
                     extensions::util::IsAppLaunchable(extension_id, context),
                     extension->from_bookmark()),
-                std::move(callback)));
+                is_placeholder_icon, std::move(callback)));
         return;
       }
 
@@ -194,7 +206,7 @@
         base::PostTaskWithTraitsAndReplyWithResult(
             FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
             base::BindOnce(&CompressedDataFromResource, ext_resource),
-            base::BindOnce(&RunCallbackWithCompressedData,
+            base::BindOnce(&RunCallbackWithCompressedData, is_placeholder_icon,
                            std::move(callback)));
         return;
       }
@@ -211,6 +223,7 @@
     apps::mojom::Publisher::LoadIconCallback callback,
     base::OnceCallback<void(apps::mojom::Publisher::LoadIconCallback)>
         fallback) {
+  constexpr bool is_placeholder_icon = false;
   // TODO(crbug.com/826982): pass size_hint_in_dip (or _in_px) on to the
   // callbacks, and re-size the decoded-from-file image)?
 
@@ -223,7 +236,8 @@
           FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
           base::BindOnce(&ReadFileAsCompressedData, path),
           base::BindOnce(&RunCallbackWithCompressedDataToUncompressWithFallback,
-                         std::move(callback), std::move(fallback)));
+                         is_placeholder_icon, std::move(callback),
+                         std::move(fallback)));
 
       return;
     }
@@ -233,7 +247,8 @@
           FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
           base::BindOnce(&ReadFileAsCompressedData, path),
           base::BindOnce(&RunCallbackWithCompressedDataWithFallback,
-                         std::move(callback), std::move(fallback)));
+                         is_placeholder_icon, std::move(callback),
+                         std::move(fallback)));
       return;
     }
   }
@@ -244,6 +259,7 @@
 void LoadIconFromResource(apps::mojom::IconCompression icon_compression,
                           int size_hint_in_dip,
                           int resource_id,
+                          bool is_placeholder_icon,
                           apps::mojom::Publisher::LoadIconCallback callback) {
   if (resource_id != 0) {
     switch (icon_compression) {
@@ -255,7 +271,7 @@
             ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
                 resource_id);
         RunCallbackWithUncompressedImageSkia(
-            std::move(callback),
+            is_placeholder_icon, std::move(callback),
             gfx::ImageSkiaOperations::CreateResizedImage(
                 *unscaled, skia::ImageOperations::RESIZE_BEST,
                 gfx::Size(size_hint_in_dip, size_hint_in_dip)));
@@ -267,7 +283,7 @@
             ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
                 resource_id);
         RunCallbackWithCompressedData(
-            std::move(callback),
+            is_placeholder_icon, std::move(callback),
             std::vector<uint8_t>(data.begin(), data.end()));
         return;
       }
diff --git a/chrome/browser/apps/app_service/app_icon_factory.h b/chrome/browser/apps/app_service/app_icon_factory.h
index 5f28e46..86e0970 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.h
+++ b/chrome/browser/apps/app_service/app_icon_factory.h
@@ -39,6 +39,7 @@
 void LoadIconFromResource(apps::mojom::IconCompression icon_compression,
                           int size_hint_in_dip,
                           int resource_id,
+                          bool is_placeholder_icon,
                           apps::mojom::Publisher::LoadIconCallback callback);
 
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/app_icon_source.cc b/chrome/browser/apps/app_service/app_icon_source.cc
index 655ae95..88ae25b4 100644
--- a/chrome/browser/apps/app_service/app_icon_source.cc
+++ b/chrome/browser/apps/app_service/app_icon_source.cc
@@ -97,8 +97,9 @@
     return;
   }
 
+  constexpr bool allow_placeholder_icon = false;
   app_service_proxy->LoadIcon(app_id, apps::mojom::IconCompression::kCompressed,
-                              size_in_dip,
+                              size_in_dip, allow_placeholder_icon,
                               base::BindOnce(&RunCallback, callback));
 }
 
diff --git a/chrome/browser/apps/app_service/app_service_proxy.cc b/chrome/browser/apps/app_service/app_service_proxy.cc
index f736bcc..6a4d6af 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy.cc
@@ -58,18 +58,20 @@
     const std::string& app_id,
     apps::mojom::IconCompression icon_compression,
     int32_t size_hint_in_dip,
+    bool allow_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback) {
   bool found = false;
   cache_.ForOneApp(app_id, [this, &icon_compression, &size_hint_in_dip,
-                            &callback, &found](const apps::AppUpdate& update) {
+                            &allow_placeholder_icon, &callback,
+                            &found](const apps::AppUpdate& update) {
     apps::mojom::IconKeyPtr icon_key = update.IconKey();
     if (icon_key.is_null()) {
       return;
     }
     found = true;
-    app_service_->LoadIcon(update.AppType(), update.AppId(),
-                           std::move(icon_key), icon_compression,
-                           size_hint_in_dip, std::move(callback));
+    app_service_->LoadIcon(update.AppType(), std::move(icon_key),
+                           icon_compression, size_hint_in_dip,
+                           allow_placeholder_icon, std::move(callback));
   });
 
   if (!found) {
diff --git a/chrome/browser/apps/app_service/app_service_proxy.h b/chrome/browser/apps/app_service/app_service_proxy.h
index 0c8c2667..3f703e8 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.h
+++ b/chrome/browser/apps/app_service/app_service_proxy.h
@@ -42,6 +42,7 @@
   void LoadIcon(const std::string& app_id,
                 apps::mojom::IconCompression icon_compression,
                 int32_t size_hint_in_dip,
+                bool allow_placeholder_icon,
                 apps::mojom::Publisher::LoadIconCallback callback);
 
   void Launch(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index 387d6f0..40eb0f0 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -25,6 +25,7 @@
 #include "components/arc/common/app.mojom.h"
 #include "components/arc/common/app_permissions.mojom.h"
 #include "content/public/common/service_manager_connection.h"
+#include "extensions/grit/extensions_browser_resources.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "services/data_decoder/public/cpp/decode_image.h"
 #include "ui/display/display.h"
@@ -168,10 +169,10 @@
   subscribers_.AddPtr(std::move(subscriber));
 }
 
-void ArcApps::LoadIcon(const std::string& app_id,
-                       apps::mojom::IconKeyPtr icon_key,
+void ArcApps::LoadIcon(apps::mojom::IconKeyPtr icon_key,
                        apps::mojom::IconCompression icon_compression,
                        int32_t size_hint_in_dip,
+                       bool allow_placeholder_icon,
                        LoadIconCallback callback) {
   if (!icon_key.is_null() &&
       (icon_key->icon_type == apps::mojom::IconType::kArc) &&
@@ -195,7 +196,8 @@
         GetCachedIconFilePath(icon_key->s_key, size_hint_in_dip),
         std::move(callback),
         base::BindOnce(&ArcApps::LoadIconFromVM, weak_ptr_factory_.GetWeakPtr(),
-                       icon_key->s_key, icon_compression, size_hint_in_dip));
+                       icon_key->s_key, icon_compression, size_hint_in_dip,
+                       allow_placeholder_icon));
     return;
   }
 
@@ -387,7 +389,16 @@
 void ArcApps::LoadIconFromVM(const std::string icon_key_s_key,
                              apps::mojom::IconCompression icon_compression,
                              int32_t size_hint_in_dip,
+                             bool allow_placeholder_icon,
                              LoadIconCallback callback) {
+  if (allow_placeholder_icon) {
+    constexpr bool is_placeholder_icon = true;
+    LoadIconFromResource(icon_compression, size_hint_in_dip,
+                         IDR_APP_DEFAULT_ICON, is_placeholder_icon,
+                         std::move(callback));
+    return;
+  }
+
   std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
       prefs_->GetApp(icon_key_s_key);
   if (app_info) {
@@ -418,8 +429,9 @@
   int size_hint_in_px = apps_util::ConvertDipToPx(size_hint_in_dip);
   int resource_id = (size_hint_in_px <= 32) ? IDR_ARC_SUPPORT_ICON_32
                                             : IDR_ARC_SUPPORT_ICON_192;
+  constexpr bool is_placeholder_icon = false;
   LoadIconFromResource(icon_compression, size_hint_in_dip, resource_id,
-                       std::move(callback));
+                       is_placeholder_icon, std::move(callback));
 }
 
 apps::mojom::AppPtr ArcApps::Convert(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/arc_apps.h b/chrome/browser/apps/app_service/arc_apps.h
index 3f428fe..22a17769 100644
--- a/chrome/browser/apps/app_service/arc_apps.h
+++ b/chrome/browser/apps/app_service/arc_apps.h
@@ -44,10 +44,10 @@
   // apps::mojom::Publisher overrides.
   void Connect(apps::mojom::SubscriberPtr subscriber,
                apps::mojom::ConnectOptionsPtr opts) override;
-  void LoadIcon(const std::string& app_id,
-                apps::mojom::IconKeyPtr icon_key,
+  void LoadIcon(apps::mojom::IconKeyPtr icon_key,
                 apps::mojom::IconCompression icon_compression,
                 int32_t size_hint_in_dip,
+                bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
   void Launch(const std::string& app_id,
               int32_t event_flags,
@@ -83,6 +83,7 @@
   void LoadIconFromVM(const std::string icon_key_s_key,
                       apps::mojom::IconCompression icon_compression,
                       int32_t size_hint_in_dip,
+                      bool allow_placeholder_icon,
                       LoadIconCallback callback);
   void LoadPlayStoreIcon(apps::mojom::IconCompression icon_compression,
                          int32_t size_hint_in_dip,
diff --git a/chrome/browser/apps/app_service/built_in_chromeos_apps.cc b/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
index aa10f848..156d590 100644
--- a/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
+++ b/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
@@ -107,17 +107,18 @@
 }
 
 void BuiltInChromeOsApps::LoadIcon(
-    const std::string& app_id,
     apps::mojom::IconKeyPtr icon_key,
     apps::mojom::IconCompression icon_compression,
     int32_t size_hint_in_dip,
+    bool allow_placeholder_icon,
     LoadIconCallback callback) {
+  constexpr bool is_placeholder_icon = false;
   if (!icon_key.is_null() &&
       (icon_key->icon_type == apps::mojom::IconType::kResource) &&
       (icon_key->u_key != 0) && (icon_key->u_key <= INT_MAX)) {
     int resource_id = static_cast<int>(icon_key->u_key);
     LoadIconFromResource(icon_compression, size_hint_in_dip, resource_id,
-                         std::move(callback));
+                         is_placeholder_icon, std::move(callback));
     return;
   }
   // On failure, we still run the callback, with the zero IconValue.
diff --git a/chrome/browser/apps/app_service/built_in_chromeos_apps.h b/chrome/browser/apps/app_service/built_in_chromeos_apps.h
index 7b5c173..6bc59ef 100644
--- a/chrome/browser/apps/app_service/built_in_chromeos_apps.h
+++ b/chrome/browser/apps/app_service/built_in_chromeos_apps.h
@@ -28,10 +28,10 @@
   // apps::mojom::Publisher overrides.
   void Connect(apps::mojom::SubscriberPtr subscriber,
                apps::mojom::ConnectOptionsPtr opts) override;
-  void LoadIcon(const std::string& app_id,
-                apps::mojom::IconKeyPtr icon_key,
+  void LoadIcon(apps::mojom::IconKeyPtr icon_key,
                 apps::mojom::IconCompression icon_compression,
                 int32_t size_hint_in_dip,
+                bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
   void Launch(const std::string& app_id,
               int32_t event_flags,
diff --git a/chrome/browser/apps/app_service/crostini_apps.cc b/chrome/browser/apps/app_service/crostini_apps.cc
index a9c6cdd..449942b 100644
--- a/chrome/browser/apps/app_service/crostini_apps.cc
+++ b/chrome/browser/apps/app_service/crostini_apps.cc
@@ -62,17 +62,18 @@
   subscribers_.AddPtr(std::move(subscriber));
 }
 
-void CrostiniApps::LoadIcon(const std::string& app_id,
-                            apps::mojom::IconKeyPtr icon_key,
+void CrostiniApps::LoadIcon(apps::mojom::IconKeyPtr icon_key,
                             apps::mojom::IconCompression icon_compression,
                             int32_t size_hint_in_dip,
+                            bool allow_placeholder_icon,
                             LoadIconCallback callback) {
   if (!icon_key.is_null()) {
     if ((icon_key->icon_type == apps::mojom::IconType::kResource) &&
         (icon_key->u_key != 0) && (icon_key->u_key <= INT_MAX)) {
       int resource_id = static_cast<int>(icon_key->u_key);
+      constexpr bool is_placeholder_icon = false;
       LoadIconFromResource(icon_compression, size_hint_in_dip, resource_id,
-                           std::move(callback));
+                           is_placeholder_icon, std::move(callback));
       return;
     }
 
@@ -87,7 +88,8 @@
           std::move(callback),
           base::BindOnce(&CrostiniApps::LoadIconFromVM,
                          weak_ptr_factory_.GetWeakPtr(), icon_key->s_key,
-                         scale_factor));
+                         icon_compression, size_hint_in_dip,
+                         allow_placeholder_icon, scale_factor));
       return;
     }
   }
@@ -142,10 +144,23 @@
 }
 
 void CrostiniApps::LoadIconFromVM(const std::string icon_key_s_key,
+                                  apps::mojom::IconCompression icon_compression,
+                                  int32_t size_hint_in_dip,
+                                  bool allow_placeholder_icon,
                                   ui::ScaleFactor scale_factor,
                                   LoadIconCallback callback) {
-  // Treat this as failure. We still run the callback, with the zero IconValue.
-  std::move(callback).Run(apps::mojom::IconValue::New());
+  if (!allow_placeholder_icon) {
+    // Treat this as failure. We still run the callback, with the zero
+    // IconValue.
+    std::move(callback).Run(apps::mojom::IconValue::New());
+    return;
+  }
+
+  // Provide a placeholder icon.
+  constexpr bool is_placeholder_icon = true;
+  LoadIconFromResource(icon_compression, size_hint_in_dip,
+                       IDR_LOGO_CROSTINI_DEFAULT_192, is_placeholder_icon,
+                       std::move(callback));
 
   // Ask the VM to load the icon (and write a cached copy to the file system).
   // The "Maybe" is because multiple requests for the same icon will be merged,
diff --git a/chrome/browser/apps/app_service/crostini_apps.h b/chrome/browser/apps/app_service/crostini_apps.h
index 148c6316..290663d 100644
--- a/chrome/browser/apps/app_service/crostini_apps.h
+++ b/chrome/browser/apps/app_service/crostini_apps.h
@@ -43,10 +43,10 @@
   // apps::mojom::Publisher overrides.
   void Connect(apps::mojom::SubscriberPtr subscriber,
                apps::mojom::ConnectOptionsPtr opts) override;
-  void LoadIcon(const std::string& app_id,
-                apps::mojom::IconKeyPtr icon_key,
+  void LoadIcon(apps::mojom::IconKeyPtr icon_key,
                 apps::mojom::IconCompression icon_compression,
                 int32_t size_hint_in_dip,
+                bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
   void Launch(const std::string& app_id,
               int32_t event_flags,
@@ -67,6 +67,9 @@
                         ui::ScaleFactor scale_factor) override;
 
   void LoadIconFromVM(const std::string icon_key_s_key,
+                      apps::mojom::IconCompression icon_compression,
+                      int32_t size_hint_in_dip,
+                      bool allow_placeholder_icon,
                       ui::ScaleFactor scale_factor,
                       LoadIconCallback callback);
 
diff --git a/chrome/browser/apps/app_service/extension_apps.cc b/chrome/browser/apps/app_service/extension_apps.cc
index 2c70697..843de94 100644
--- a/chrome/browser/apps/app_service/extension_apps.cc
+++ b/chrome/browser/apps/app_service/extension_apps.cc
@@ -124,10 +124,10 @@
   subscribers_.AddPtr(std::move(subscriber));
 }
 
-void ExtensionApps::LoadIcon(const std::string& app_id,
-                             apps::mojom::IconKeyPtr icon_key,
+void ExtensionApps::LoadIcon(apps::mojom::IconKeyPtr icon_key,
                              apps::mojom::IconCompression icon_compression,
                              int32_t size_hint_in_dip,
+                             bool allow_placeholder_icon,
                              LoadIconCallback callback) {
   if (!icon_key.is_null() &&
       (icon_key->icon_type == apps::mojom::IconType::kExtension) &&
diff --git a/chrome/browser/apps/app_service/extension_apps.h b/chrome/browser/apps/app_service/extension_apps.h
index 0f093be..26d66fe 100644
--- a/chrome/browser/apps/app_service/extension_apps.h
+++ b/chrome/browser/apps/app_service/extension_apps.h
@@ -50,10 +50,10 @@
   // apps::mojom::Publisher overrides.
   void Connect(apps::mojom::SubscriberPtr subscriber,
                apps::mojom::ConnectOptionsPtr opts) override;
-  void LoadIcon(const std::string& app_id,
-                apps::mojom::IconKeyPtr icon_key,
+  void LoadIcon(apps::mojom::IconKeyPtr icon_key,
                 apps::mojom::IconCompression icon_compression,
                 int32_t size_hint_in_dip,
+                bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
   void Launch(const std::string& app_id,
               int32_t event_flags,
diff --git a/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher_browsertest.cc b/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher_browsertest.cc
index 4fcf9fd..a22e6c8 100644
--- a/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher_browsertest.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher_browsertest.cc
@@ -105,19 +105,9 @@
             cloud_policy_manager->core()->client());
     cloud_policy_client->SetDMToken("fake-dm-token");
     cloud_policy_client->client_id_ = "client-id";
-
-    test_url_loader_factory_ =
-        std::make_unique<network::TestURLLoaderFactory>();
-    test_shared_loader_factory_ =
-        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-            test_url_loader_factory_.get());
   }
 
-  void TearDownOnMainThread() override {
-    if (test_shared_loader_factory_)
-      test_shared_loader_factory_->Detach();
-    user_manager_enabler_.reset();
-  }
+  void TearDownOnMainThread() override { user_manager_enabler_.reset(); }
 
   chromeos::FakeChromeUserManager* GetFakeUserManager() const {
     return static_cast<chromeos::FakeChromeUserManager*>(
@@ -128,7 +118,8 @@
                      bool* output_fetch_success,
                      std::string* output_auth_code) {
     base::RunLoop run_loop;
-    fetcher->SetURLLoaderFactoryForTesting(test_shared_loader_factory_);
+    fetcher->SetURLLoaderFactoryForTesting(
+        test_url_loader_factory_.GetSafeWeakWrapper());
     fetcher->Fetch(base::Bind(
         [](bool* output_fetch_success, std::string* output_auth_code,
            base::RunLoop* run_loop, bool fetch_success,
@@ -145,16 +136,14 @@
   }
 
   network::TestURLLoaderFactory* test_url_loader_factory() {
-    return test_url_loader_factory_.get();
+    return &test_url_loader_factory_;
   }
 
  private:
   // Whether to connect the CloudPolicyClient.
   CloudPolicyClientSetup cloud_policy_client_setup_;
 
-  std::unique_ptr<network::TestURLLoaderFactory> test_url_loader_factory_;
-  scoped_refptr<network::WeakWrapperSharedURLLoaderFactory>
-      test_shared_loader_factory_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
   std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
 
   DISALLOW_COPY_AND_ASSIGN(ArcRobotAuthCodeFetcherBrowserTest);
diff --git a/chrome/browser/chromeos/arc/process/arc_process_service.cc b/chrome/browser/chromeos/arc/process/arc_process_service.cc
index 143e318..684d328ac 100644
--- a/chrome/browser/chromeos/arc/process/arc_process_service.cc
+++ b/chrome/browser/chromeos/arc/process/arc_process_service.cc
@@ -32,6 +32,7 @@
 #include "base/trace_event/trace_event.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
+#include "components/arc/arc_util.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
 #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
@@ -68,6 +69,12 @@
 
 std::vector<ArcProcess> GetArcSystemProcessList() {
   std::vector<ArcProcess> ret_processes;
+
+  if (arc::IsArcVmEnabled()) {
+    // TODO(b/122992194): Fix this for ARCVM.
+    return ret_processes;
+  }
+
   const base::ProcessIterator::ProcessEntries& entry_list =
       base::ProcessIterator(nullptr).Snapshot();
   const base::ProcessId arc_init_pid = GetArcInitProcessId(entry_list);
@@ -152,17 +159,29 @@
     std::vector<mojom::RunningAppProcessInfoPtr> processes) {
   std::vector<ArcProcess> ret_processes;
   for (const auto& entry : processes) {
-    const auto it = pid_map.find(entry->pid);
-    // The nspid could be missing due to race condition. For example, the
-    // process is still running when we get the process snapshot and ends when
-    // we update the nspid to pid mapping.
-    if (it == pid_map.end() || it->second == base::kNullProcessId) {
-      continue;
+    base::ProcessId pid;
+    if (arc::IsArcVmEnabled()) {
+      // When VM is enabled, there is no external pid. Set the pid here to the
+      // guest pid. Setting the pid to zero was considered but the task manager
+      // groups tasks by pid and this can cause incorrect aggregated stats to
+      // be displayed. The task manager will handle these cases by checking if
+      // the task is in VM (via Task::IsRunningInVM) and know to partition off
+      // these processes.
+      pid = entry->pid;
+    } else {
+      const auto it = pid_map.find(entry->pid);
+      // The nspid could be missing due to race condition. For example, the
+      // process is still running when we get the process snapshot and ends when
+      // we update the nspid to pid mapping.
+      if (it == pid_map.end() || it->second == base::kNullProcessId) {
+        continue;
+      }
+      pid = it->second;
     }
     // Constructs the ArcProcess instance if the mapping is found.
-    ArcProcess arc_process(entry->pid, pid_map.at(entry->pid),
-                           entry->process_name, entry->process_state,
-                           entry->is_focused, entry->last_activity_time);
+    ArcProcess arc_process(entry->pid, pid, entry->process_name,
+                           entry->process_state, entry->is_focused,
+                           entry->last_activity_time);
     // |entry->packages| is provided only when process.mojom's verion is >=4.
     if (entry->packages) {
       for (const auto& package : *entry->packages) {
@@ -178,26 +197,28 @@
     scoped_refptr<ArcProcessService::NSPidToPidMap> nspid_map,
     std::vector<mojom::RunningAppProcessInfoPtr> processes) {
   ArcProcessService::NSPidToPidMap& pid_map = *nspid_map;
-  // Cleanup dead pids in the cache |pid_map|.
-  std::unordered_set<ProcessId> nspid_to_remove;
-  for (const auto& entry : pid_map) {
-    nspid_to_remove.insert(entry.first);
-  }
-  bool unmapped_nspid = false;
-  for (const auto& entry : processes) {
-    // erase() returns 0 if coudln't find the key. It means a new process.
-    if (nspid_to_remove.erase(entry->pid) == 0) {
-      pid_map[entry->pid] = base::kNullProcessId;
-      unmapped_nspid = true;
+  if (!arc::IsArcVmEnabled()) {
+    // Cleanup dead pids in the cache |pid_map|.
+    std::unordered_set<ProcessId> nspid_to_remove;
+    for (const auto& entry : pid_map) {
+      nspid_to_remove.insert(entry.first);
     }
-  }
-  for (const auto& entry : nspid_to_remove) {
-    pid_map.erase(entry);
-  }
+    bool unmapped_nspid = false;
+    for (const auto& entry : processes) {
+      // erase() returns 0 if coudln't find the key. It means a new process.
+      if (nspid_to_remove.erase(entry->pid) == 0) {
+        pid_map[entry->pid] = base::kNullProcessId;
+        unmapped_nspid = true;
+      }
+    }
+    for (const auto& entry : nspid_to_remove) {
+      pid_map.erase(entry);
+    }
 
-  // The operation is costly so avoid calling it when possible.
-  if (unmapped_nspid) {
-    UpdateNspidToPidMap(nspid_map);
+    // The operation is costly so avoid calling it when possible.
+    if (unmapped_nspid) {
+      UpdateNspidToPidMap(nspid_map);
+    }
   }
 
   return FilterProcessList(pid_map, std::move(processes));
@@ -207,34 +228,36 @@
 UpdateAndReturnMemoryInfo(
     scoped_refptr<ArcProcessService::NSPidToPidMap> nspid_map,
     memory_instrumentation::mojom::GlobalMemoryDumpPtr dump) {
-  ArcProcessService::NSPidToPidMap& pid_map = *nspid_map;
-  // Cleanup dead processes in pid_map
-  // TODO(wvk) should we be cleaning dead processes here too ?
-  base::flat_set<ProcessId> nspid_to_remove;
-  for (const auto& entry : pid_map)
-    nspid_to_remove.insert(entry.first);
+  if (!arc::IsArcVmEnabled()) {
+    ArcProcessService::NSPidToPidMap& pid_map = *nspid_map;
+    // Cleanup dead processes in pid_map
+    // TODO(wvk) should we be cleaning dead processes here too ?
+    base::flat_set<ProcessId> nspid_to_remove;
+    for (const auto& entry : pid_map)
+      nspid_to_remove.insert(entry.first);
 
-  bool unmapped_nspid = false;
-  for (const auto& proc : dump->process_dumps) {
-    // erase() returns 0 if couldn't find the key (new process)
-    if (nspid_to_remove.erase(proc->pid) == 0) {
-      pid_map[proc->pid] = base::kNullProcessId;
-      unmapped_nspid = true;
+    bool unmapped_nspid = false;
+    for (const auto& proc : dump->process_dumps) {
+      // erase() returns 0 if couldn't find the key (new process)
+      if (nspid_to_remove.erase(proc->pid) == 0) {
+        pid_map[proc->pid] = base::kNullProcessId;
+        unmapped_nspid = true;
+      }
     }
-  }
-  for (const auto& old_nspid : nspid_to_remove)
-    pid_map.erase(old_nspid);
+    for (const auto& old_nspid : nspid_to_remove)
+      pid_map.erase(old_nspid);
 
-  if (unmapped_nspid)
-    UpdateNspidToPidMap(nspid_map);
+    if (unmapped_nspid)
+      UpdateNspidToPidMap(nspid_map);
 
-  // Return memory info only for processes that have a mapping nspid->pid
-  for (auto& proc : dump->process_dumps) {
-    auto it = pid_map.find(proc->pid);
-    proc->pid = it == pid_map.end() ? kNullProcessId : it->second;
+    // Return memory info only for processes that have a mapping nspid->pid
+    for (auto& proc : dump->process_dumps) {
+      auto it = pid_map.find(proc->pid);
+      proc->pid = it == pid_map.end() ? kNullProcessId : it->second;
+    }
+    base::EraseIf(dump->process_dumps,
+                  [](const auto& proc) { return proc->pid == kNullProcessId; });
   }
-  base::EraseIf(dump->process_dumps,
-                [](const auto& proc) { return proc->pid == kNullProcessId; });
   return memory_instrumentation::GlobalMemoryDump::MoveFrom(std::move(dump));
 }
 
@@ -393,8 +416,11 @@
     return;
   }
   std::vector<uint32_t> nspids;
-  for (const auto& proc : procs)
-    nspids.push_back(proc.nspid());
+  if (!arc::IsArcVmEnabled()) {
+    for (const auto& proc : procs)
+      nspids.push_back(proc.nspid());
+  }
+  // TODO(b/122992194): Fix this for ARCVM.
   process_instance->RequestSystemProcessMemoryInfo(
       nspids,
       base::BindOnce(&ArcProcessService::OnReceiveMemoryInfo,
diff --git a/chrome/browser/chromeos/drive/file_system_util.cc b/chrome/browser/chromeos/drive/file_system_util.cc
index a80838c..b23a7821 100644
--- a/chrome/browser/chromeos/drive/file_system_util.cc
+++ b/chrome/browser/chromeos/drive/file_system_util.cc
@@ -32,7 +32,7 @@
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths_internal.h"
 #include "chromeos/constants/chromeos_constants.h"
-#include "components/browser_sync/profile_sync_service.h"
+#include "components/browser_sync/browser_sync_switches.h"
 #include "components/drive/chromeos/file_system_interface.h"
 #include "components/drive/drive.pb.h"
 #include "components/drive/drive_pref_names.h"
@@ -223,7 +223,7 @@
 
   // Disable drive if sync is disabled by command line flag. Outside tests, this
   // only occurs in cases already handled by the gaia account check above.
-  if (!browser_sync::ProfileSyncService::IsSyncAllowedByFlag())
+  if (!switches::IsSyncAllowedByFlag())
     return false;
 
   return true;
diff --git a/chrome/browser/chromeos/events/event_rewriter_unittest.cc b/chrome/browser/chromeos/events/event_rewriter_unittest.cc
index c7b1d1a4..dd1fb78 100644
--- a/chrome/browser/chromeos/events/event_rewriter_unittest.cc
+++ b/chrome/browser/chromeos/events/event_rewriter_unittest.cc
@@ -1276,21 +1276,21 @@
   rewriter_->set_ime_keyboard_for_testing(&ime_keyboard);
   EXPECT_FALSE(ime_keyboard.caps_lock_is_enabled_);
 
-  // On Chrome OS, CapsLock is mapped to F16 with Mod3Mask.
+  // On Chrome OS, CapsLock is mapped to CapsLock with Mod3Mask.
   EXPECT_EQ(GetExpectedResultAsString(
                 ui::ET_KEY_PRESSED, ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
                 ui::EF_CAPS_LOCK_ON | ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK),
             GetRewrittenEventAsString(rewriter_, ui::ET_KEY_PRESSED,
-                                      ui::VKEY_F16, ui::DomCode::F16,
-                                      ui::EF_MOD3_DOWN, ui::DomKey::F16));
+                                      ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
+                                      ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK));
   EXPECT_FALSE(ime_keyboard.caps_lock_is_enabled_);
 
   EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CAPITAL,
                                       ui::DomCode::CAPS_LOCK, ui::EF_NONE,
                                       ui::DomKey::CAPS_LOCK),
             GetRewrittenEventAsString(rewriter_, ui::ET_KEY_RELEASED,
-                                      ui::VKEY_F16, ui::DomCode::F16,
-                                      ui::EF_MOD3_DOWN, ui::DomKey::F16));
+                                      ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
+                                      ui::EF_MOD3_DOWN, ui::DomKey::CAPS_LOCK));
   EXPECT_TRUE(ime_keyboard.caps_lock_is_enabled_);
 
   // Remap Caps Lock to Control.
@@ -1314,22 +1314,6 @@
             GetRewrittenEventAsString(rewriter_, ui::ET_KEY_RELEASED,
                                       ui::VKEY_CAPITAL, ui::DomCode::CAPS_LOCK,
                                       ui::EF_NONE, ui::DomKey::CAPS_LOCK));
-
-  // Press F16.
-  EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL,
-                                      ui::DomCode::CONTROL_LEFT,
-                                      ui::EF_CONTROL_DOWN, ui::DomKey::CONTROL),
-            GetRewrittenEventAsString(rewriter_, ui::ET_KEY_PRESSED,
-                                      ui::VKEY_F16, ui::DomCode::F16,
-                                      ui::EF_MOD3_DOWN, ui::DomKey::F16));
-
-  // Release F16.
-  EXPECT_EQ(GetExpectedResultAsString(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL,
-                                      ui::DomCode::CONTROL_LEFT, ui::EF_NONE,
-                                      ui::DomKey::CONTROL),
-            GetRewrittenEventAsString(rewriter_, ui::ET_KEY_RELEASED,
-                                      ui::VKEY_F16, ui::DomCode::F16,
-                                      ui::EF_MOD3_DOWN, ui::DomKey::F16));
 }
 
 TEST_F(EventRewriterTest, TestRewriteDiamondKey) {
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 588c07c..ac13ccd 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -477,7 +477,10 @@
         TestCase("checkPlayFilesContextMenu"),
         TestCase("checkPlayFilesContextMenu").EnableMyFilesVolume(),
         TestCase("checkLinuxFilesContextMenu"),
-        TestCase("checkLinuxFilesContextMenu").EnableMyFilesVolume()));
+        TestCase("checkLinuxFilesContextMenu").EnableMyFilesVolume(),
+        TestCase("checkRemovableRootContextMenu"),
+        TestCase("checkUsbContextMenu"),
+        TestCase("checkPartitionContextMenu")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     Delete, /* delete.js */
diff --git a/chrome/browser/chromeos/file_manager/path_util.cc b/chrome/browser/chromeos/file_manager/path_util.cc
index aec6e53..24846573 100644
--- a/chrome/browser/chromeos/file_manager/path_util.cc
+++ b/chrome/browser/chromeos/file_manager/path_util.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
+#include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/fileapi/external_file_url_util.h"
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -205,6 +206,22 @@
   return false;
 }
 
+bool MigrateToDriveFs(Profile* profile,
+                      const base::FilePath& old_path,
+                      base::FilePath* new_path) {
+  const auto* user = chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+  auto* integration_service =
+      drive::DriveIntegrationServiceFactory::FindForProfile(profile);
+  if (!base::FeatureList::IsEnabled(chromeos::features::kDriveFs) ||
+      !integration_service || !integration_service->is_enabled() || !user ||
+      !user->GetAccountId().HasAccountIdKey()) {
+    return false;
+  }
+  *new_path = integration_service->GetMountPointPath();
+  return drive::util::GetDriveMountPointPath(profile).AppendRelativePath(
+      old_path, new_path);
+}
+
 std::string GetDownloadsMountPointName(Profile* profile) {
   // To distinguish profiles in multi-profile session, we append user name hash
   // to "Downloads". Note that some profiles (like login or test profiles)
@@ -500,6 +517,33 @@
                                .Append(l10n_util::GetStringUTF8(
                                    IDS_FILE_BROWSER_DRIVE_COMPUTERS_LABEL))
                                .value())) {
+  } else if (drive_integration_service &&
+             ReplacePrefix(&result,
+                           drive::util::GetDriveMountPointPath(profile)
+                               .Append(kDriveFsDirRoot)
+                               .value(),
+                           base::FilePath(kDisplayNameGoogleDrive)
+                               .Append(l10n_util::GetStringUTF8(
+                                   IDS_FILE_BROWSER_DRIVE_MY_DRIVE_LABEL))
+                               .value())) {
+  } else if (drive_integration_service &&
+             ReplacePrefix(&result,
+                           drive::util::GetDriveMountPointPath(profile)
+                               .Append(kDriveFsDirTeamDrives)
+                               .value(),
+                           base::FilePath(kDisplayNameGoogleDrive)
+                               .Append(l10n_util::GetStringUTF8(
+                                   IDS_FILE_BROWSER_DRIVE_TEAM_DRIVES_LABEL))
+                               .value())) {
+  } else if (drive_integration_service &&
+             ReplacePrefix(&result,
+                           drive::util::GetDriveMountPointPath(profile)
+                               .Append(kDriveFsDirComputers)
+                               .value(),
+                           base::FilePath(kDisplayNameGoogleDrive)
+                               .Append(l10n_util::GetStringUTF8(
+                                   IDS_FILE_BROWSER_DRIVE_COMPUTERS_LABEL))
+                               .value())) {
   } else if (ReplacePrefix(&result, kAndroidFilesPath,
                            l10n_util::GetStringUTF8(
                                IDS_FILE_BROWSER_ANDROID_FILES_ROOT_LABEL))) {
diff --git a/chrome/browser/chromeos/file_manager/path_util.h b/chrome/browser/chromeos/file_manager/path_util.h
index f082d4a0..89067af 100644
--- a/chrome/browser/chromeos/file_manager/path_util.h
+++ b/chrome/browser/chromeos/file_manager/path_util.h
@@ -61,6 +61,14 @@
                                    const base::FilePath& old_path,
                                    base::FilePath* new_path);
 
+// Convers |old_path| in /special/drive-<hash> to |new_path| in
+// /media/fuse/drivefs-<id>. Returns true if path is changed else
+// returns false if |old_path| was not inside Drive, and |new_path| is
+// undefined.
+bool MigrateToDriveFs(Profile* profile,
+                      const base::FilePath& old_path,
+                      base::FilePath* new_path);
+
 // The canonical mount point name for "Downloads" folder.
 std::string GetDownloadsMountPointName(Profile* profile);
 
diff --git a/chrome/browser/chromeos/file_manager/path_util_unittest.cc b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
index 9000c4a..fa85091 100644
--- a/chrome/browser/chromeos/file_manager/path_util_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
@@ -238,6 +238,21 @@
             "/media/fuse/drivefs-84675c855b63e12f384d45f033826980/"
             "Computers/My Other Computer/bar"));
 
+    EXPECT_EQ("Google Drive \u203a My Drive \u203a foo",
+              GetPathDisplayTextForSettings(
+                  &profile2, "/special/drive-0123456789abcdef/root/foo"));
+    EXPECT_EQ(
+        "Google Drive \u203a Team Drives \u203a A Team Drive \u203a foo",
+        GetPathDisplayTextForSettings(
+            &profile2,
+            "/special/drive-0123456789abcdef/team_drives/A Team Drive/foo"));
+
+    EXPECT_EQ(
+        "Google Drive \u203a Computers \u203a My Other Computer \u203a bar",
+        GetPathDisplayTextForSettings(
+            &profile2,
+            "/special/drive-0123456789abcdef/Computers/My Other Computer/bar"));
+
     TestingProfile guest_profile(base::FilePath("/home/chronos/guest"));
     guest_profile.SetGuestSession(true);
     guest_profile.set_profile_name("$guest");
@@ -370,6 +385,49 @@
       FilePath::FromUTF8Unsafe("/home/chronos/user/dl"), &path));
 }
 
+TEST_F(FileManagerPathUtilTest, MigrateToDriveFs) {
+  base::FilePath home("/home/chronos/u-0123456789abcdef");
+  base::FilePath other("/some/other/path");
+  base::FilePath old_drive("/special/drive-0123456789abcdef");
+  base::FilePath my_drive = old_drive.Append("root");
+  base::FilePath file_in_my_drive = old_drive.Append("root").Append("file.txt");
+
+  // DriveFS disabled, no changes.
+  base::FilePath result;
+  {
+    drive::DriveIntegrationServiceFactory::GetForProfile(profile_.get())
+        ->SetEnabled(true);
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndDisableFeature(chromeos::features::kDriveFs);
+    EXPECT_FALSE(MigrateToDriveFs(profile_.get(), other, &result));
+    EXPECT_FALSE(MigrateToDriveFs(profile_.get(), my_drive, &result));
+  }
+  // MyFilesVolume enabled, migrate paths under Downloads.
+  {
+    TestingProfile profile2(base::FilePath("/home/chronos/u-0123456789abcdef"));
+    chromeos::FakeChromeUserManager user_manager;
+    user_manager.AddUser(
+        AccountId::FromUserEmailGaiaId(profile2.GetProfileUserName(), "12345"));
+    PrefService* prefs = profile2.GetPrefs();
+    prefs->SetString(drive::prefs::kDriveFsProfileSalt, "a");
+    drive::DriveIntegrationServiceFactory::GetForProfile(&profile2)->SetEnabled(
+        true);
+
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndEnableFeature(chromeos::features::kDriveFs);
+    EXPECT_FALSE(MigrateToDriveFs(&profile2, other, &result));
+    EXPECT_TRUE(MigrateToDriveFs(&profile2, my_drive, &result));
+    EXPECT_EQ(base::FilePath(
+                  "/media/fuse/drivefs-84675c855b63e12f384d45f033826980/root"),
+              result);
+    EXPECT_TRUE(MigrateToDriveFs(&profile2, file_in_my_drive, &result));
+    EXPECT_EQ(
+        base::FilePath("/media/fuse/drivefs-84675c855b63e12f384d45f033826980/"
+                       "root/file.txt"),
+        result);
+  }
+}
+
 TEST_F(FileManagerPathUtilTest, ConvertFileSystemURLToPathInsideCrostini) {
   content::TestServiceManagerContext service_manager_context;
 
diff --git a/chrome/browser/chromeos/scheduler_configuration_manager.cc b/chrome/browser/chromeos/scheduler_configuration_manager.cc
index f80cfc4..ca6ef4a 100644
--- a/chrome/browser/chromeos/scheduler_configuration_manager.cc
+++ b/chrome/browser/chromeos/scheduler_configuration_manager.cc
@@ -7,6 +7,8 @@
 #include <string>
 
 #include "base/bind.h"
+#include "base/metrics/field_trial_params.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/dbus/debug_daemon_client.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -15,6 +17,11 @@
 
 namespace chromeos {
 
+namespace {
+constexpr base::FeatureParam<std::string> kSchedulerConfigurationParam{
+    &features::kSchedulerConfiguration, "config", ""};
+}  // namespace
+
 SchedulerConfigurationManager::SchedulerConfigurationManager(
     DebugDaemonClient* debug_daemon_client,
     PrefService* local_state)
@@ -34,6 +41,10 @@
 // static
 void SchedulerConfigurationManager::RegisterLocalStatePrefs(
     PrefRegistrySimple* registry) {
+  // Ideally the pref would be registered specifying the default provided via
+  // the feature parameter. This is unfortunately not possible though because
+  // the feature API initialization depends on the local state PrefService, so
+  // this function runs before feature parameters are available.
   registry->RegisterStringPref(prefs::kSchedulerConfiguration, std::string());
 }
 
@@ -55,10 +66,16 @@
     return;
   }
 
+  // Determine the effective configuration name. Prefer the value from local
+  // state if present, feature parameter if otherwise, hard-coded default if
+  // no configuration is present.
   std::string config_name;
   PrefService* local_state = observer_.prefs();
+  std::string feature_param_value = kSchedulerConfigurationParam.Get();
   if (local_state->HasPrefPath(prefs::kSchedulerConfiguration)) {
     config_name = local_state->GetString(prefs::kSchedulerConfiguration);
+  } else if (!feature_param_value.empty()) {
+    config_name = feature_param_value;
   } else {
     config_name = debugd::scheduler_configuration::kPerformanceScheduler;
   }
diff --git a/chrome/browser/chromeos/scheduler_configuration_manager_unittest.cc b/chrome/browser/chromeos/scheduler_configuration_manager_unittest.cc
index e1507d6..d614f591 100644
--- a/chrome/browser/chromeos/scheduler_configuration_manager_unittest.cc
+++ b/chrome/browser/chromeos/scheduler_configuration_manager_unittest.cc
@@ -6,7 +6,9 @@
 
 #include <memory>
 
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/dbus/fake_debug_daemon_client.h"
 #include "components/prefs/testing_pref_service.h"
@@ -79,4 +81,20 @@
             debug_daemon_client_.scheduler_configuration_name());
 }
 
+TEST_F(SchedulerConfigurationManagerTest, FinchDefault) {
+  auto feature_list = std::make_unique<base::test::ScopedFeatureList>();
+  feature_list->InitAndEnableFeatureWithParameters(
+      features::kSchedulerConfiguration, {{"config", "finch"}});
+
+  // Finch parameter selects the default.
+  SchedulerConfigurationManager manager(&debug_daemon_client_, &local_state_);
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_EQ("finch", debug_daemon_client_.scheduler_configuration_name());
+
+  // Config values override finch default.
+  local_state_.SetString(prefs::kSchedulerConfiguration, "config");
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_EQ("config", debug_daemon_client_.scheduler_configuration_name());
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/download/download_prefs.cc b/chrome/browser/download/download_prefs.cc
index 53d7b76..f1c854a 100644
--- a/chrome/browser/download/download_prefs.cc
+++ b/chrome/browser/download/download_prefs.cc
@@ -143,6 +143,9 @@
     } else if (file_manager::util::MigrateFromDownloadsToMyFiles(
                    profile_, current, &migrated)) {
       prefs->SetFilePath(path_pref[i], migrated);
+    } else if (file_manager::util::MigrateToDriveFs(profile_, current,
+                                                    &migrated)) {
+      prefs->SetFilePath(path_pref[i], migrated);
     }
   }
 
@@ -450,6 +453,14 @@
 base::FilePath DownloadPrefs::SanitizeDownloadTargetPath(
     const base::FilePath& path) const {
 #if defined(OS_CHROMEOS)
+  base::FilePath migrated_drive_path;
+  // Managed prefs may force a legacy Drive path as the download path. Ensure
+  // the path is valid when DriveFS is enabled.
+  if (file_manager::util::MigrateToDriveFs(profile_, path,
+                                           &migrated_drive_path)) {
+    return SanitizeDownloadTargetPath(migrated_drive_path);
+  }
+
   // If |path| isn't absolute, fall back to the default directory.
   base::FilePath profile_myfiles_path =
       file_manager::util::GetMyFilesFolderForProfile(profile_);
diff --git a/chrome/browser/drive/drive_notification_manager_factory.cc b/chrome/browser/drive/drive_notification_manager_factory.cc
index e413a78f..a4ffda5c 100644
--- a/chrome/browser/drive/drive_notification_manager_factory.cc
+++ b/chrome/browser/drive/drive_notification_manager_factory.cc
@@ -8,7 +8,7 @@
 #include "chrome/browser/invalidation/deprecated_profile_invalidation_provider_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "components/browser_sync/profile_sync_service.h"
+#include "components/browser_sync/browser_sync_switches.h"
 #include "components/drive/drive_notification_manager.h"
 #include "components/invalidation/impl/profile_invalidation_provider.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -27,7 +27,7 @@
 DriveNotificationManager*
 DriveNotificationManagerFactory::GetForBrowserContext(
     content::BrowserContext* context) {
-  if (!browser_sync::ProfileSyncService::IsSyncAllowedByFlag())
+  if (!switches::IsSyncAllowedByFlag())
     return NULL;
   if (!invalidation::DeprecatedProfileInvalidationProviderFactory::
           GetForProfile(Profile::FromBrowserContext(context))) {
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc
index db22228..b5659c4 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc
@@ -50,4 +50,8 @@
   ASSERT_TRUE(RunExtensionTest("extension_with_no_ruleset")) << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(DeclarativeNetRequestAPItest, DynamicRules) {
+  ASSERT_TRUE(RunExtensionTest("dynamic_rules")) << message_;
+}
+
 }  // namespace
diff --git a/chrome/browser/extensions/content_script_apitest.cc b/chrome/browser/extensions/content_script_apitest.cc
index 05f2028d..16eb061 100644
--- a/chrome/browser/extensions/content_script_apitest.cc
+++ b/chrome/browser/extensions/content_script_apitest.cc
@@ -152,8 +152,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(ContentScriptApiTest);
 };
 
@@ -931,6 +929,51 @@
   EXPECT_EQ(123, content::EvalJs(web_contents, "123"));
 }
 
+namespace {
+
+enum class BindingsType { kNative, kJavaScript };
+
+// Test fixture for testing messaging APIs. Is parameterized to allow testing
+// with and without native (C++-based) extension bindings.
+class ContentScriptMessagingApiTest
+    : public ContentScriptApiTest,
+      public ::testing::WithParamInterface<BindingsType> {
+ protected:
+  ContentScriptMessagingApiTest() {
+    if (GetParam() == BindingsType::kNative) {
+      scoped_feature_list_.InitAndEnableFeature(
+          extensions_features::kNativeCrxBindings);
+    } else {
+      scoped_feature_list_.InitAndDisableFeature(
+          extensions_features::kNativeCrxBindings);
+    }
+  }
+
+  ~ContentScriptMessagingApiTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+}  // namespace
+
+// Verifies how the messaging API works with content scripts.
+IN_PROC_BROWSER_TEST_P(ContentScriptMessagingApiTest, Test) {
+  ASSERT_TRUE(StartEmbeddedTestServer());
+  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII(
+      "content_scripts/other_extensions/message_echoer_allows_by_default")));
+  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII(
+      "content_scripts/other_extensions/message_echoer_allows")));
+  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII(
+      "content_scripts/other_extensions/message_echoer_denies")));
+  ASSERT_TRUE(RunExtensionTest("content_scripts/messaging")) << message_;
+}
+
+INSTANTIATE_TEST_CASE_P(,
+                        ContentScriptMessagingApiTest,
+                        testing::Values(BindingsType::kNative,
+                                        BindingsType::kJavaScript));
+
 // Test fixture which sets a custom NTP Page.
 // TODO(karandeepb): Similar logic to set up a custom NTP is used elsewhere as
 // well. Abstract this away into a reusable test fixture class.
diff --git a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
index 7e7cd95..c2c2b7a 100644
--- a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
@@ -48,6 +48,7 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
 #include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/identity_test_utils.h"
 #include "services/identity/public/cpp/primary_account_mutator.h"
 #endif
 
@@ -182,12 +183,9 @@
 #else
     // Mock a signed-in user. This is used by the UserCloudPolicyStore to pass
     // the account id to the UserCloudPolicyValidator.
-    auto* primary_account_mutator =
-        IdentityManagerFactory::GetForProfile(browser()->profile())
-            ->GetPrimaryAccountMutator();
-    primary_account_mutator->LegacyStartSigninWithRefreshTokenForPrimaryAccount(
-        "", "account_id", PolicyBuilder::kFakeUsername,
-        base::OnceCallback<void(const std::string&)>());
+    identity::SetPrimaryAccount(
+        IdentityManagerFactory::GetForProfile(browser()->profile()),
+        PolicyBuilder::kFakeUsername);
 
     UserCloudPolicyManager* policy_manager =
         UserCloudPolicyManagerFactory::GetForBrowserContext(
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index a2a8aa3..f23c4a7 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/pref_names.h"
-#include "components/browser_sync/profile_sync_service.h"
+#include "components/browser_sync/browser_sync_switches.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/language/core/browser/pref_names.h"
@@ -24,6 +24,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/sync/base/sync_prefs.h"
+#include "components/sync/driver/sync_service.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/browser/storage_partition.h"
@@ -280,8 +281,7 @@
   // No ProfileSyncService created yet - we don't want to create one, so just
   // infer the accessible state by looking at prefs/command line flags.
   syncer::SyncPrefs prefs(GetPrefs());
-  return browser_sync::ProfileSyncService::IsSyncAllowedByFlag() &&
-         !prefs.IsManaged();
+  return switches::IsSyncAllowedByFlag() && !prefs.IsManaged();
 }
 
 void Profile::MaybeSendDestroyedNotification() {
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index ba7b435..db1989f 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -122,6 +122,7 @@
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/cors_origin_pattern_setter.h"
 #include "content/public/browser/dom_storage_context.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/permission_controller.h"
@@ -227,6 +228,7 @@
 using base::TimeDelta;
 using bookmarks::BookmarkModel;
 using content::BrowserThread;
+using content::CorsOriginPatternSetter;
 using content::DownloadManagerDelegate;
 
 namespace {
@@ -325,52 +327,6 @@
 }
 #endif  // defined(OS_CHROMEOS)
 
-// A class used to make an asynchronous Mojo call with cloned patterns for each
-// StoragePartition iteration. |this| instance will be destructed when all
-// existing asynchronous Mojo calls made in SetLists() are done, and |closure|
-// will be invoked on destructing |this|.
-class CorsOriginPatternSetter
-    : public base::RefCounted<CorsOriginPatternSetter> {
- public:
-  CorsOriginPatternSetter(
-      const url::Origin& source_origin,
-      std::vector<network::mojom::CorsOriginPatternPtr> allow_patterns,
-      std::vector<network::mojom::CorsOriginPatternPtr> block_patterns,
-      base::OnceClosure closure)
-      : source_origin_(source_origin),
-        allow_patterns_(std::move(allow_patterns)),
-        block_patterns_(std::move(block_patterns)),
-        closure_(std::move(closure)) {}
-
-  void SetLists(content::StoragePartition* partition) {
-    partition->GetNetworkContext()->SetCorsOriginAccessListsForOrigin(
-        source_origin_, ClonePatterns(allow_patterns_),
-        ClonePatterns(block_patterns_),
-        base::BindOnce([](scoped_refptr<CorsOriginPatternSetter> setter) {},
-                       base::RetainedRef(this)));
-  }
-
-  static std::vector<network::mojom::CorsOriginPatternPtr> ClonePatterns(
-      const std::vector<network::mojom::CorsOriginPatternPtr>& patterns) {
-    std::vector<network::mojom::CorsOriginPatternPtr> cloned_patterns;
-    cloned_patterns.reserve(patterns.size());
-    for (const auto& item : patterns)
-      cloned_patterns.push_back(item.Clone());
-    return cloned_patterns;
-  }
-
- private:
-  friend class base::RefCounted<CorsOriginPatternSetter>;
-
-  ~CorsOriginPatternSetter() { std::move(closure_).Run(); }
-
-  const url::Origin source_origin_;
-  const std::vector<network::mojom::CorsOriginPatternPtr> allow_patterns_;
-  const std::vector<network::mojom::CorsOriginPatternPtr> block_patterns_;
-
-  base::OnceClosure closure_;
-};
-
 }  // namespace
 
 // static
diff --git a/chrome/browser/resources/app_management/router.js b/chrome/browser/resources/app_management/router.js
index 88fb59b..06532b0 100644
--- a/chrome/browser/resources/app_management/router.js
+++ b/chrome/browser/resources/app_management/router.js
@@ -45,6 +45,8 @@
     },
   },
 
+  urlParsed_: false,
+
   observers: [
     'onUrlChanged_(path_, queryParams_)',
     'onStateChanged_(currentPageType_, selectedAppId_, searchTerm_)',
@@ -81,6 +83,9 @@
 
   /** @private */
   onStateChanged_: function() {
+    if (!this.urlParsed_) {
+      return;
+    }
     this.debounce('publishUrl', this.publishUrl_);
   },
 
@@ -149,5 +154,6 @@
     } else {
       this.dispatch(app_management.actions.changePage(newPage));
     }
+    this.urlParsed_ = true;
   },
 });
diff --git a/chrome/browser/resources/chromeos/camera/src/css/main.css b/chrome/browser/resources/chromeos/camera/src/css/main.css
index 6e9318d..38217d7 100644
--- a/chrome/browser/resources/chromeos/camera/src/css/main.css
+++ b/chrome/browser/resources/chromeos/camera/src/css/main.css
@@ -83,8 +83,8 @@
   -webkit-appearance: none;
 }
 
-button:focus:after,
-input:focus:after {
+button:focus::after,
+input:focus::after {
   border: 2px solid rgba(37, 129, 223, 0.7);
   border-radius: 4px;
   bottom: -3px;
@@ -101,8 +101,8 @@
 }
 
 .circle button,
-.circle button:focus:after,
-.circle input:focus:after {
+.circle button:focus::after,
+.circle input:focus::after {
   border-radius: 50%;
 }
 
@@ -558,7 +558,7 @@
   transform: translateY(-50%);
 }
 
-body._4x4 #preview-grid-horizontal:before {
+body._4x4 #preview-grid-horizontal::before {
   border-bottom: 1px solid white;
   content: '';
   height: 0;
@@ -579,9 +579,9 @@
 }
 
 body.grid._4x4 #preview-grid-horizontal,
-body.grid._4x4 #preview-grid-horizontal:before,
+body.grid._4x4 #preview-grid-horizontal::before,
 body.gridsettings._4x4 #preview-grid-horizontal,
-body.gridsettings._4x4 #preview-grid-horizontal:before {
+body.gridsettings._4x4 #preview-grid-horizontal::before {
   height: 50%;
 }
 
@@ -596,7 +596,7 @@
   width: 100%;
 }
 
-body._4x4 #preview-grid-vertical:before {
+body._4x4 #preview-grid-vertical::before {
   border-right: 1px solid white;
   bottom: 0;
   content: '';
@@ -617,26 +617,26 @@
 }
 
 body.grid._4x4 #preview-grid-vertical,
-body.grid._4x4 #preview-grid-vertical:before,
+body.grid._4x4 #preview-grid-vertical::before,
 body.gridsettings._4x4 #preview-grid-vertical,
-body.gridsettings._4x4 #preview-grid-vertical:before {
+body.gridsettings._4x4 #preview-grid-vertical::before {
   width: 50%;
 }
 
 #preview-grid-horizontal.animate,
-#preview-grid-horizontal.animate:before {
+#preview-grid-horizontal.animate::before {
   transition: height 500ms, visibility 500ms;
 }
 
 #preview-grid-vertical.animate,
-#preview-grid-vertical.animate:before {
+#preview-grid-vertical.animate::before {
   transition: width 500ms, visibility 500ms;
 }
 
 body:not(.grid):not(.gridsettings) #preview-grid-horizontal,
-body:not(.grid):not(.gridsettings) #preview-grid-horizontal:before,
+body:not(.grid):not(.gridsettings) #preview-grid-horizontal::before,
 body:not(.grid):not(.gridsettings) #preview-grid-vertical,
-body:not(.grid):not(.gridsettings) #preview-grid-vertical:before {
+body:not(.grid):not(.gridsettings) #preview-grid-vertical::before {
   visibility: hidden;
 }
 
@@ -657,7 +657,7 @@
 }
 
 #record-time .icon {
-  background-color: #ea4335;
+  background-color: rgb(234, 67, 53);
   border-radius: 50%;
   flex-shrink: 0;
   height: 6px;
@@ -776,7 +776,7 @@
 #tooltip {
   background: rgba(241, 243, 244, 0.8);
   border-radius: 2px;
-  color: #202124;
+  color: rgb(32, 33, 36);
   font-family: 'Roboto', sans-serif;
   font-size: 12px;
   left: 0;
@@ -796,7 +796,7 @@
 }
 
 #toast {
-  background: #1e1e23;
+  background: rgb(30, 30, 35);
   border-radius: 16px;
   color: white;
   font-family: 'Roboto', sans-serif;
@@ -817,7 +817,8 @@
 }
 
 @keyframes toast-spoken {
-  0%, 100% {
+  0%,
+  100% {
     opacity: 0;
   }
 }
@@ -826,7 +827,8 @@
   0% {
     opacity: 0;
   }
-  10%, 90% {
+  10%,
+  90% {
     opacity: 0.9;
   }
   100% {
@@ -839,17 +841,17 @@
   display: flex;
   flex-direction: column;
   height: 100%;
-  left: 0px;
+  left: 0;
   min-width: 360px;
   opacity: 0.9;
   position: absolute;
-  top: 0px;
+  top: 0;
 }
 
 .menu-header,
 .menu-item {
   align-items: center;
-  color: #f1f3f4;
+  color: rgb(241, 243, 244);
   display: flex;
   flex-shrink: 0;
   font-family: 'Roboto', sans-serif;
@@ -876,7 +878,7 @@
 }
 
 .menu-item .description {
-  color: #bdc1c6;
+  color: rgb(189, 193, 198);
   margin-top: 5px;
 }
 
@@ -892,10 +894,10 @@
   display: inline;
 }
 
-.menu-item input:before {
+.menu-item input::before {
   border-radius: 50%;
   bottom: 13px;
-  box-shadow: 0px 0px 0px 2px #f1f3f4;
+  box-shadow: 0 0 0 2px rgb(241, 243, 244);
   content: '';
   left: 13px;
   position: absolute;
@@ -903,19 +905,19 @@
   top: 13px;
 }
 
-.menu-item input:checked:before {
+.menu-item input:checked::before {
   background-clip: padding-box;
-  background-color: #f1f3f4;
+  background-color: rgb(241, 243, 244);
   border: 4px solid transparent;
   bottom: 12px;
-  box-shadow: 0px 0px 0px 1px #f1f3f4;
+  box-shadow: 0 0 0 1px rgb(241, 243, 244);
   left: 12px;
   right: 12px;
   top: 12px;
   transition: border-width 100ms ease-in;
 }
 
-.menu-item:focus:after {
+.menu-item:focus::after {
   left: 2px;
   right: 2px;
 }
@@ -985,7 +987,7 @@
 }
 
 #dialog #dialog-msg {
-  color: #202124;
+  color: rgb(32, 33, 36);
   cursor: text;
   font-family: 'Roboto', sans-serif;
   font-size: 14px;
@@ -1022,7 +1024,7 @@
 #dialog-buttons button {
   background-color: white;
   border-style: solid;
-  color: #2581df;
+  color: rgb(37, 129, 223);
   font-family: 'Roboto', sans-serif;
   font-size: 12px;
   margin: 4px;
@@ -1030,12 +1032,12 @@
 }
 
 #dialog-buttons button:focus {
-  background-color: #2581df;
-  border-color: #2581df;
+  background-color: rgb(37, 129, 223);
+  border-color: rgb(37, 129, 223);
   color: white;
 }
 
-#dialog-buttons button:focus:after {
+#dialog-buttons button:focus::after {
   border: none;
 }
 
diff --git a/chrome/browser/resources/print_preview/new/pages_settings.js b/chrome/browser/resources/print_preview/new/pages_settings.js
index 245d22e..c2d4bfd 100644
--- a/chrome/browser/resources/print_preview/new/pages_settings.js
+++ b/chrome/browser/resources/print_preview/new/pages_settings.js
@@ -113,8 +113,13 @@
     'input-change': 'onInputChange_',
   },
 
-  /** @private {string} */
-  lastInput_: '',
+  /**
+   * True if the user's last valid input should be restored to the custom input
+   * field. Cleared when the input is set automatically, or the user manually
+   * clears the field.
+   * @private {boolean}
+   */
+  restoreLastInput_: true,
 
   /**
    * Initialize |selectedValue| in attached() since this doesn't observe
@@ -135,6 +140,9 @@
    * @private
    */
   onInputChange_: function(e) {
+    if (this.inputString_ !== e.detail) {
+      this.restoreLastInput_ = true;
+    }
     this.inputString_ = e.detail;
   },
 
@@ -253,7 +261,6 @@
         }
       }
     }
-    this.lastInput_ = this.inputString_;
     this.errorState_ = PagesInputErrorState.NO_ERROR;
     this.pagesToPrint_ = pages;
   },
@@ -358,7 +365,11 @@
   onCustomInputBlur_: function() {
     this.resetAndUpdate();
     if (this.errorState_ === PagesInputErrorState.EMPTY) {
-      this.$$('cr-input').value = this.lastInput_;
+      // Update with all pages.
+      this.$$('cr-input').value = this.getAllPagesString_();
+      this.inputString_ = this.getAllPagesString_();
+      this.resetString();
+      this.restoreLastInput_ = false;
     }
   },
 
@@ -411,13 +422,22 @@
     return !this.customSelected_ || this.controlsDisabled_;
   },
 
+  /**
+   * @return {string} A string representing the full page range.
+   * @private
+   */
+  getAllPagesString_: function() {
+    return this.pageCount === 1 ? '1' : `1-${this.pageCount}`;
+  },
+
   /** @private */
   onCustomSelectedChange_: function() {
-    if (!this.customSelected_ &&
-        (this.errorState_ == PagesInputErrorState.INVALID_SYNTAX ||
-         this.errorState_ == PagesInputErrorState.OUT_OF_BOUNDS)) {
-      this.$$('cr-input').value = '';
+    if ((this.customSelected_ && !this.restoreLastInput_) ||
+        this.errorState_ !== PagesInputErrorState.NO_ERROR) {
+      this.restoreLastInput_ = true;
       this.inputString_ = '';
+      this.$$('cr-input').value = '';
+      this.resetString();
     }
     this.updatePagesToPrint_();
   }
diff --git a/chrome/browser/resources/print_preview/new/scaling_settings.html b/chrome/browser/resources/print_preview/new/scaling_settings.html
index 3d1b792..58c12da 100644
--- a/chrome/browser/resources/print_preview/new/scaling_settings.html
+++ b/chrome/browser/resources/print_preview/new/scaling_settings.html
@@ -30,7 +30,8 @@
         </select>
       </div>
     </print-preview-settings-section>
-    <iron-collapse opened="[[customSelected_]]">
+    <iron-collapse opened="[[customSelected_]]"
+        on-transitionend="onCollapseChanged_">
       <print-preview-number-settings-section
           max-value="200" min-value="10" default-value="100"
           disabled$="[[inputDisabled_(disabled, inputValid_, customSelected_)]]"
diff --git a/chrome/browser/resources/print_preview/new/scaling_settings.js b/chrome/browser/resources/print_preview/new/scaling_settings.js
index a891b32..4d2e396 100644
--- a/chrome/browser/resources/print_preview/new/scaling_settings.js
+++ b/chrome/browser/resources/print_preview/new/scaling_settings.js
@@ -162,4 +162,11 @@
   computeCustomSelected_: function() {
     return this.selectedValue === ScalingValue.CUSTOM.toString();
   },
+
+  /** @private */
+  onCollapseChanged_: function() {
+    if (this.customSelected_) {
+      this.$$('print-preview-number-settings-section').getInput().focus();
+    }
+  },
 });
diff --git a/chrome/browser/resources/usb_internals/BUILD.gn b/chrome/browser/resources/usb_internals/BUILD.gn
new file mode 100644
index 0000000..763a0faa
--- /dev/null
+++ b/chrome/browser/resources/usb_internals/BUILD.gn
@@ -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.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+  deps = [
+    ":usb_internals",
+  ]
+}
+
+js_library("usb_internals") {
+  sources = [
+    "usb_internals.js",
+  ]
+
+  deps = [
+    "//chrome/browser/ui/webui/usb_internals:mojo_bindings_js_externs",
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:util",
+    "//ui/webui/resources/js/cr/ui:tabs",
+  ]
+}
diff --git a/chrome/browser/resources/usb_internals/usb_internals.css b/chrome/browser/resources/usb_internals/usb_internals.css
index c11885b..af12290b 100644
--- a/chrome/browser/resources/usb_internals/usb_internals.css
+++ b/chrome/browser/resources/usb_internals/usb_internals.css
@@ -3,6 +3,7 @@
  * found in the LICENSE file.
  */
 
+/* Devices Tab */
 table.styled-table {
   border-collapse: collapse;
 }
@@ -11,12 +12,12 @@
 .styled-table th,
 .styled-table td {
   border: 1px solid #777;
-  padding-left: 4px;
-  padding-right: 4px;
+  padding-inline-end: 4px;
+  padding-inline-start: 4px;
 }
 
 .styled-table th {
-  text-align: left;
+  text-align: start;
 }
 
 .page-section {
diff --git a/chrome/browser/resources/usb_internals/usb_internals.html b/chrome/browser/resources/usb_internals/usb_internals.html
index 2bcc4830..1c84c37 100644
--- a/chrome/browser/resources/usb_internals/usb_internals.html
+++ b/chrome/browser/resources/usb_internals/usb_internals.html
@@ -1,55 +1,88 @@
 <!doctype html>
 <html lang="en">
+
 <head>
   <meta charset="utf-8">
   <title>USB Internals</title>
   <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
+  <link rel="stylesheet" href="chrome://resources/css/tabs.css">
   <link rel="stylesheet" href="usb_internals.css">
+
+  <script src="chrome://resources/js/assert.js"></script>
   <script src="chrome://resources/js/cr.js"></script>
+  <script src="chrome://resources/js/promise_resolver.js"></script>
+  <script src="chrome://resources/js/cr/ui.js"></script>
+  <script src="chrome://resources/js/cr/ui/focus_outline_manager.js"></script>
+  <script src="chrome://resources/js/cr/ui/tabs.js"></script>
   <script src="chrome://resources/js/mojo_bindings.js"></script>
   <script src="chrome://resources/js/util.js"></script>
   <script src="device/usb/public/mojom/device_manager_test.mojom.js"></script>
   <script src="chrome/browser/ui/webui/usb_internals/usb_internals.mojom.js">
   </script>
-  <script src="usb_internals.js"></script>
 </head>
-<body>
-  <p>
-    <table class="styled-table">
-      <thead>
-        <tr>
-          <th>Name</th>
-          <th>Serial number</th>
-          <th>Landing page</th>
-          <th>
-        </tr>
-      </thead>
-      <tbody id="test-device-list">
-      </tbody>
-    </table>
-  </p>
 
-  <div class="page-section">
-    <strong>Add a test device:</strong>
-    <form id="add-test-device-form" action="">
-      <p>
-        <label>
-          Name: <input id="test-device-name" type="text" size="40">
-        </label>
-      </p>
-      <p>
-        <label>
-          Serial number: <input id="test-device-serial" type="text" size="40">
-        </label>
-      </p>
-      <p>
-        <label>
-          Landing page:
-          <input id="test-device-landing-page" type="text" size="40">
-        </label>
-      </p>
-      <button type="submit">Add</button> <span id="add-test-device-result"></span>
-    </form>
-  </div>
+<body>
+  <tabbox>
+    <tabs>
+      <tab>Test Devices</tab>
+    </tabs>
+    <tabpanels>
+      <tabpanel>
+        <!-- Test Devices -->
+        <h2>Test Devices</h2>
+        <p>
+          <table class="styled-table">
+            <thead>
+              <tr>
+                <th>Name</th>
+                <th>Serial number</th>
+                <th>Landing page</th>
+                <th>
+              </tr>
+            </thead>
+            <tbody id="test-device-list">
+            </tbody>
+
+            <template id="test-device-row">
+              <tr>
+                <td></td>
+                <td></td>
+                <td></td>
+                <td><button>Remove</button></td>
+              </tr>
+            </template>
+
+          </table>
+        </p>
+        <div class="page-section">
+          <strong>Add a test device:</strong>
+          <form id="add-test-device-form" action="">
+            <p>
+              <label>
+                Name: <input id="test-device-name" type="text" size="40">
+              </label>
+            </p>
+            <p>
+              <label>
+                Serial number:
+                <input id="test-device-serial" type="text" size="40">
+              </label>
+            </p>
+            <p>
+              <label>
+                Landing page:
+                <input id="test-device-landing-page" type="text" size="40">
+              </label>
+            </p>
+            <button type="submit">Add</button>
+            <span id="add-test-device-result"></span>
+          </form>
+        </div>
+      </tabpanel>
+    </tabpanels>
+  </tabbox>
+
+  <script src="usb_internals.js"></script>
 </body>
-</html>
+
+</html>
\ No newline at end of file
diff --git a/chrome/browser/resources/usb_internals/usb_internals.js b/chrome/browser/resources/usb_internals/usb_internals.js
index b9ea066f..67e63a8 100644
--- a/chrome/browser/resources/usb_internals/usb_internals.js
+++ b/chrome/browser/resources/usb_internals/usb_internals.js
@@ -5,67 +5,75 @@
 /**
  * Javascript for usb_internals.html, served from chrome://usb-internals/.
  */
+cr.define('usb_internals', function() {
+  class UsbInternals {
+    constructor() {
+      // Connection to the UsbInternalsPageHandler instance running in the
+      // browser process.
+      this.usbManagerTest = null;
 
-(function() {
-// Connection to the UsbInternalsPageHandler instance running in the browser
-// process.
-let usbManagerTest = null;
+      const pageHandler = new mojom.UsbInternalsPageHandlerPtr;
+      Mojo.bindInterface(
+          mojom.UsbInternalsPageHandler.name,
+          mojo.makeRequest(pageHandler).handle);
 
-function refreshDeviceList() {
-  usbManagerTest.getTestDevices().then(function(response) {
-    const tableBody = $('test-device-list');
-    tableBody.innerHTML = '';
-    for (const device of response.devices) {
-      const row = document.createElement('tr');
-      const name = document.createElement('td');
-      const serialNumber = document.createElement('td');
-      const landingPage = document.createElement('td');
-      const remove = document.createElement('td');
-      const removeButton = document.createElement('button');
-      name.textContent = device.name;
-      serialNumber.textContent = device.serialNumber;
-      landingPage.textContent = device.landingPage.url;
-      removeButton.addEventListener('click', async function() {
-        await usbManagerTest.removeDeviceForTesting(device.guid);
-        refreshDeviceList();
+      this.usbManagerTest = new device.mojom.UsbDeviceManagerTestPtr;
+      pageHandler.bindTestInterface(mojo.makeRequest(this.usbManagerTest));
+
+      cr.ui.decorate('tabbox', cr.ui.TabBox);
+      $('add-test-device-form').addEventListener('submit', (event) => {
+        this.addTestDevice(event);
       });
-      removeButton.textContent = 'Remove';
-      row.appendChild(name);
-      row.appendChild(serialNumber);
-      row.appendChild(landingPage);
-      remove.appendChild(removeButton);
-      row.appendChild(remove);
-      tableBody.appendChild(row);
+      this.refreshDeviceList();
     }
-  });
-}
 
-function addTestDevice(event) {
-  usbManagerTest
-      .addDeviceForTesting(
+    async refreshDeviceList() {
+      const response = await this.usbManagerTest.getTestDevices();
+
+      const tableBody = $('test-device-list');
+      tableBody.innerHTML = '';
+
+      const rowTemplate = document.querySelector('#test-device-row');
+      const td = rowTemplate.content.querySelectorAll('td');
+
+      for (const device of response.devices) {
+        td[0].textContent = device.name;
+        td[1].textContent = device.serialNumber;
+        td[2].textContent = device.landingPage.url;
+
+        const clone = document.importNode(rowTemplate.content, true);
+
+        const removeButton = clone.querySelector('button');
+        removeButton.addEventListener('click', async () => {
+          await this.usbManagerTest.removeDeviceForTesting(device.guid);
+          this.refreshDeviceList();
+        });
+
+        tableBody.appendChild(clone);
+      }
+    }
+
+    async addTestDevice(event) {
+      event.preventDefault();
+
+      const response = await this.usbManagerTest.addDeviceForTesting(
           $('test-device-name').value, $('test-device-serial').value,
-          $('test-device-landing-page').value)
-      .then(function(response) {
-        if (response.success) {
-          refreshDeviceList();
-        }
+          $('test-device-landing-page').value);
+      if (response.success) {
+        this.refreshDeviceList();
+      }
 
-        $('add-test-device-result').textContent = response.message;
-        $('add-test-device-result').className =
-            response.success ? 'action-success' : 'action-failure';
-      });
-  event.preventDefault();
-}
+      $('add-test-device-result').textContent = response.message;
+      $('add-test-device-result').className =
+          response.success ? 'action-success' : 'action-failure';
+    }
+  }
 
-document.addEventListener('DOMContentLoaded', function() {
-  const pageHandler = new mojom.UsbInternalsPageHandlerPtr;
-  Mojo.bindInterface(
-      mojom.UsbInternalsPageHandler.name, mojo.makeRequest(pageHandler).handle);
-
-  usbManagerTest = new device.mojom.UsbDeviceManagerTestPtr;
-  pageHandler.bindTestInterface(mojo.makeRequest(usbManagerTest));
-
-  $('add-test-device-form').addEventListener('submit', addTestDevice);
-  refreshDeviceList();
+  return {
+    UsbInternals,
+  };
 });
-})();
+
+document.addEventListener('DOMContentLoaded', () => {
+  new usb_internals.UsbInternals();
+});
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/art.jpg b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/art.jpg
new file mode 100644
index 0000000..2514b17e
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/art.jpg
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/cityscape.jpg b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/cityscape.jpg
new file mode 100644
index 0000000..4c36cf1
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/cityscape.jpg
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/geometric_shapes.jpg b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/geometric_shapes.jpg
new file mode 100644
index 0000000..6092436
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/geometric_shapes.jpg
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/landscape.jpg b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/landscape.jpg
new file mode 100644
index 0000000..340ddff
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/landscape.jpg
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/life.jpg b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/life.jpg
new file mode 100644
index 0000000..58c9aa4
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/life.jpg
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/ntp_background_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/ntp_background_proxy.js
index 6df82b4f..250fb518 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/ntp_background_proxy.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/ntp_background_proxy.js
@@ -7,6 +7,7 @@
    * @typedef {{
    *   id: number,
    *   imageUrl: string,
+   *   thumbnailClass: string,
    *   title: string,
    * }}
    */
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.html b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.html
index 5a136ea..8bea421 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.html
@@ -69,6 +69,26 @@
         flex: 1;
       }
 
+      .art {
+        background-image: url(../images/ntp_thumbnails/art.jpg);
+      }
+
+      .cityscape {
+        background-image: url(../images/ntp_thumbnails/cityscape.jpg);
+      }
+
+      .geometric-shapes {
+        background-image: url(../images/ntp_thumbnails/geometric_shapes.jpg);
+      }
+
+      .landscape {
+        background-image: url(../images/ntp_thumbnails/landscape.jpg);
+      }
+
+      .life {
+        background-image: url(../images/ntp_thumbnails/life.jpg);
+      }
+
       .ntp-background-title {
         border-top: var(--cr-separator-line);
         font-size: 14px;
@@ -109,7 +129,7 @@
             on-click="onBackgroundClick_"
             on-keyup="onBackgroundKeyUp_"
             on-pointerdown="onBackgroundPointerDown_">
-          <div class="ntp-background-thumbnail"></div>
+          <div class$="ntp-background-thumbnail [[item.thumbnailClass]]"></div>
           <div class="ntp-background-title">[[item.title]]</div>
         </button>
       </template>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.js b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.js
index c2b39ae..75579b8 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.js
@@ -35,6 +35,7 @@
     const defaultBackground = {
       id: -1,
       imageUrl: '',
+      thumbnailClass: '',
       title: this.i18n('ntpBackgroundDefault'),
     };
     this.selectedBackground_ = defaultBackground;
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd b/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
index 0ba3e7f..55b4f6a 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
+++ b/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
@@ -38,6 +38,11 @@
       <include name="IDR_NUX_GOOGLE_APPS_YOUTUBE_2X" file="images\youtube_2x.png" type="BINDATA" />
       <include name="IDR_NUX_NTP_BACKGROUND_LOGO_1X" file="images\ntp_background_1x.png" type="BINDATA" />
       <include name="IDR_NUX_NTP_BACKGROUND_LOGO_2X" file="images\ntp_background_2x.png" type="BINDATA" />
+      <include name="IDR_NUX_NTP_BACKGROUND_THUMBNAIL_ART" file="images\ntp_thumbnails\art.jpg" type="BINDATA" />
+      <include name="IDR_NUX_NTP_BACKGROUND_THUMBNAIL_CITYSCAPE" file="images\ntp_thumbnails\cityscape.jpg" type="BINDATA" />
+      <include name="IDR_NUX_NTP_BACKGROUND_THUMBNAIL_GEOMETRIC_SHAPES" file="images\ntp_thumbnails\geometric_shapes.jpg" type="BINDATA" />
+      <include name="IDR_NUX_NTP_BACKGROUND_THUMBNAIL_LANDSCAPE" file="images\ntp_thumbnails\landscape.jpg" type="BINDATA" />
+      <include name="IDR_NUX_NTP_BACKGROUND_THUMBNAIL_LIFE" file="images\ntp_thumbnails\life.jpg" type="BINDATA" />
       <include name="IDR_NUX_SET_AS_DEFAULT_ILLUSTRATION_1X" file="images\set_as_default_illustration_1x.png" type="BINDATA" />
       <include name="IDR_NUX_SET_AS_DEFAULT_ILLUSTRATION_2X" file="images\set_as_default_illustration_2x.png" type="BINDATA" />
       <include name="IDR_NUX_SET_AS_DEFAULT_LOGO_1X" file="images\set_as_default_1x.png" type="BINDATA" />
diff --git a/chrome/browser/signin/chrome_signin_client.cc b/chrome/browser/signin/chrome_signin_client.cc
index b8763a3..22ee0f5 100644
--- a/chrome/browser/signin/chrome_signin_client.cc
+++ b/chrome/browser/signin/chrome_signin_client.cc
@@ -338,15 +338,6 @@
 #endif
 }
 
-void ChromeSigninClient::AfterCredentialsCopied() {
-  if (signin_util::IsForceSigninEnabled()) {
-    // The signout after credential copy won't open UserManager after all
-    // browser window are closed. Because the browser window will be opened for
-    // the new profile soon.
-    should_display_user_manager_ = false;
-  }
-}
-
 void ChromeSigninClient::SetReadyForDiceMigration(bool is_ready) {
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
   AccountConsistencyModeManager::GetForProfile(profile_)
diff --git a/chrome/browser/signin/chrome_signin_client.h b/chrome/browser/signin/chrome_signin_client.h
index 4080fb0a..bf666eb 100644
--- a/chrome/browser/signin/chrome_signin_client.h
+++ b/chrome/browser/signin/chrome_signin_client.h
@@ -82,7 +82,6 @@
   void OnConnectionChanged(network::mojom::ConnectionType type) override;
 #endif
 
-  void AfterCredentialsCopied() override;
   void SetReadyForDiceMigration(bool is_ready) override;
 
   // Used in tests to override the URLLoaderFactory returned by
diff --git a/chrome/browser/signin/chrome_signin_client_unittest.cc b/chrome/browser/signin/chrome_signin_client_unittest.cc
index 4110a6c..c9b0da4 100644
--- a/chrome/browser/signin/chrome_signin_client_unittest.cc
+++ b/chrome/browser/signin/chrome_signin_client_unittest.cc
@@ -190,42 +190,6 @@
   PreSignOut(source_metric, delete_metric);
 }
 
-TEST_F(ChromeSigninClientSignoutTest, SignOutWithoutManager) {
-  signin_metrics::ProfileSignout source_metric =
-      signin_metrics::ProfileSignout::USER_CLICKED_SIGNOUT_SETTINGS;
-  signin_metrics::SignoutDelete delete_metric =
-      signin_metrics::SignoutDelete::IGNORE_METRIC;
-
-  // Call the method below instead calling SigninManager::CopyCredentialsFrom,
-  // keeping the same behavior.
-  client_->AfterCredentialsCopied();
-
-  EXPECT_CALL(*client_, ShowUserManager(browser()->profile()->GetPath()))
-      .Times(0);
-  EXPECT_CALL(*client_, LockForceSigninProfile(browser()->profile()->GetPath()))
-      .Times(1);
-  EXPECT_CALL(
-      *client_,
-      SignOutCallback(source_metric, delete_metric,
-                      SigninClient::SignoutDecision::ALLOW_SIGNOUT))
-      .Times(1);
-
-  PreSignOut(source_metric, delete_metric);
-
-  ::testing::Mock::VerifyAndClearExpectations(client_.get());
-
-  EXPECT_CALL(*client_, ShowUserManager(browser()->profile()->GetPath()))
-      .Times(1);
-  EXPECT_CALL(*client_, LockForceSigninProfile(browser()->profile()->GetPath()))
-      .Times(1);
-  EXPECT_CALL(
-      *client_,
-      SignOutCallback(source_metric, delete_metric,
-                      SigninClient::SignoutDecision::ALLOW_SIGNOUT))
-      .Times(1);
-  PreSignOut(source_metric, delete_metric);
-}
-
 TEST_F(ChromeSigninClientSignoutTest, SignOutWithoutForceSignin) {
   signin_util::SetForceSigninForTesting(false);
   CreateClient(browser()->profile());
diff --git a/chrome/browser/signin/dice_browsertest.cc b/chrome/browser/signin/dice_browsertest.cc
index 6e0128eb..85c8f2e 100644
--- a/chrome/browser/signin/dice_browsertest.cc
+++ b/chrome/browser/signin/dice_browsertest.cc
@@ -659,10 +659,6 @@
       GetIdentityManager()->HasAccountWithRefreshToken(GetMainAccountID()));
   // Sync should not be enabled.
   EXPECT_TRUE(GetIdentityManager()->GetPrimaryAccountId().empty());
-  EXPECT_TRUE(GetIdentityManager()
-                  ->GetPrimaryAccountMutator()
-                  ->LegacyPrimaryAccountForAuthInProgress()
-                  .account_id.empty());
 
   EXPECT_EQ(1, reconcilor_blocked_count_);
   WaitForReconcilorUnblockedCount(1);
@@ -906,10 +902,6 @@
       browser()->profile()));
 
   EXPECT_FALSE(GetIdentityManager()->GetPrimaryAccountId().empty());
-  EXPECT_TRUE(GetIdentityManager()
-                  ->GetPrimaryAccountMutator()
-                  ->LegacyPrimaryAccountForAuthInProgress()
-                  .account_id.empty());
   EXPECT_TRUE(
       GetIdentityManager()->HasAccountWithRefreshToken(GetMainAccountID()));
   EXPECT_FALSE(GetIdentityManager()->GetAccountsWithRefreshTokens().empty());
@@ -929,10 +921,6 @@
       browser()->profile()));
 
   EXPECT_TRUE(GetIdentityManager()->GetPrimaryAccountId().empty());
-  EXPECT_TRUE(GetIdentityManager()
-                  ->GetPrimaryAccountMutator()
-                  ->LegacyPrimaryAccountForAuthInProgress()
-                  .account_id.empty());
   EXPECT_FALSE(
       GetIdentityManager()->HasAccountWithRefreshToken(GetMainAccountID()));
   EXPECT_TRUE(GetIdentityManager()->GetAccountsWithRefreshTokens().empty());
diff --git a/chrome/browser/signin/force_signin_verifier.cc b/chrome/browser/signin/force_signin_verifier.cc
index 5cc5185..971e5aa 100644
--- a/chrome/browser/signin/force_signin_verifier.cc
+++ b/chrome/browser/signin/force_signin_verifier.cc
@@ -142,8 +142,7 @@
   // Do not close window if there is ongoing reauth. If it fails later, the
   // signin process should take care of the signout.
   auto* primary_account_mutator = identity_manager_->GetPrimaryAccountMutator();
-  if (!primary_account_mutator ||
-      primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress())
+  if (!primary_account_mutator)
     return;
   primary_account_mutator->ClearPrimaryAccount(
       identity::PrimaryAccountMutator::ClearAccountsAction::kRemoveAll,
diff --git a/chrome/browser/signin/signin_promo_util.cc b/chrome/browser/signin/signin_promo_util.cc
index 7013b71..745aae4 100644
--- a/chrome/browser/signin/signin_promo_util.cc
+++ b/chrome/browser/signin/signin_promo_util.cc
@@ -41,13 +41,7 @@
   // Display the signin promo if the user is not signed in.
   identity::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(original_profile);
-  if (identity_manager->HasPrimaryAccount() ||
-      identity_manager->GetPrimaryAccountMutator()
-          ->LegacyIsPrimaryAccountAuthInProgress()) {
-    return false;
-  }
-
-  return true;
+  return !identity_manager->HasPrimaryAccount();
 #endif
 }
 
diff --git a/chrome/browser/sync/profile_sync_service_factory.cc b/chrome/browser/sync/profile_sync_service_factory.cc
index 7ac80d4..444e0f3 100644
--- a/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/chrome/browser/sync/profile_sync_service_factory.cc
@@ -41,6 +41,7 @@
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/undo/bookmark_undo_service_factory.h"
 #include "chrome/browser/web_data_service_factory.h"
+#include "components/browser_sync/browser_sync_switches.h"
 #include "components/browser_sync/profile_sync_components_factory_impl.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/invalidation/impl/invalidation_switches.h"
@@ -115,7 +116,7 @@
 // static
 syncer::SyncService* ProfileSyncServiceFactory::GetSyncServiceForProfile(
     Profile* profile) {
-  if (!ProfileSyncService::IsSyncAllowedByFlag()) {
+  if (!switches::IsSyncAllowedByFlag()) {
     return nullptr;
   }
 
diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc
index 87322ea..38f673d 100644
--- a/chrome/browser/sync/sync_ui_util.cc
+++ b/chrome/browser/sync/sync_ui_util.cc
@@ -20,7 +20,6 @@
 #include "components/sync/protocol/sync_protocol_error.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "services/identity/public/cpp/identity_manager.h"
-#include "services/identity/public/cpp/primary_account_mutator.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace sync_ui_util {
@@ -138,13 +137,14 @@
 }
 
 // status_label and link_label must either be both null or both non-null.
-MessageType GetStatusLabelsImpl(const syncer::SyncService* service,
-                                identity::IdentityManager* identity_manager,
-                                const bool is_user_signout_allowed,
-                                const GoogleServiceAuthError& auth_error,
-                                base::string16* status_label,
-                                base::string16* link_label,
-                                ActionType* action_type) {
+MessageType GetStatusLabelsImpl(
+    const syncer::SyncService* service,
+    const identity::IdentityManager* identity_manager,
+    bool is_user_signout_allowed,
+    const GoogleServiceAuthError& auth_error,
+    base::string16* status_label,
+    base::string16* link_label,
+    ActionType* action_type) {
   DCHECK(service);
   DCHECK_EQ(status_label == nullptr, link_label == nullptr);
 
@@ -152,10 +152,6 @@
     return PRE_SYNCED;
   }
 
-  // Needed to check the state of the authentication process below.
-  const auto* primary_account_mutator =
-      identity_manager->GetPrimaryAccountMutator();
-
   syncer::SyncStatus status;
   service->QueryDetailedSyncStatus(&status);
 
@@ -176,16 +172,6 @@
       return SYNC_ERROR;
     }
 
-    // For auth errors first check if an auth is in progress.
-    if (primary_account_mutator &&
-        primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress()) {
-      if (status_label) {
-        *status_label =
-            l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL);
-      }
-      return PRE_SYNCED;
-    }
-
     // Since there is no auth in progress, check for an auth error first.
     if (auth_error.state() != GoogleServiceAuthError::NONE) {
       if (status_label && link_label) {
@@ -241,14 +227,8 @@
       *status_label = l10n_util::GetStringUTF16(IDS_SYNC_NTP_SETUP_IN_PROGRESS);
     }
 
-    if (primary_account_mutator &&
-        primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress()) {
-      if (status_label) {
-        *status_label =
-            l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL);
-      }
-    } else if (auth_error.state() != GoogleServiceAuthError::NONE &&
-               auth_error.state() != GoogleServiceAuthError::TWO_FACTOR) {
+    if (auth_error.state() != GoogleServiceAuthError::NONE &&
+        auth_error.state() != GoogleServiceAuthError::TWO_FACTOR) {
       if (status_label && link_label) {
         GetStatusForAuthError(auth_error, status_label, link_label,
                               action_type);
diff --git a/chrome/browser/sync/sync_ui_util_unittest.cc b/chrome/browser/sync/sync_ui_util_unittest.cc
index d073398..4d72e7b 100644
--- a/chrome/browser/sync/sync_ui_util_unittest.cc
+++ b/chrome/browser/sync/sync_ui_util_unittest.cc
@@ -22,7 +22,6 @@
 #include "services/identity/public/cpp/identity_manager.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
 #include "services/identity/public/cpp/identity_test_utils.h"
-#include "services/identity/public/cpp/primary_account_mutator.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -34,7 +33,6 @@
 enum DistinctState {
   STATUS_CASE_SETUP_IN_PROGRESS,
   STATUS_CASE_SETUP_ERROR,
-  STATUS_CASE_AUTHENTICATING,
   STATUS_CASE_AUTH_ERROR,
   STATUS_CASE_PROTOCOL_ERROR,
   STATUS_CASE_PASSPHRASE_ERROR,
@@ -44,9 +42,7 @@
   NUMBER_OF_STATUS_CASES
 };
 
-const char kTestGaiaId[] = "gaia-id-test_user@test.com";
 const char kTestUser[] = "test_user@test.com";
-const char kRefreshToken[] = "refresh_token";
 
 }  // namespace
 
@@ -78,30 +74,6 @@
       service->SetDetailedSyncStatus(false, syncer::SyncEngine::Status());
       return;
     }
-    case STATUS_CASE_AUTHENTICATING: {
-      service->SetFirstSetupComplete(true);
-      service->SetTransportState(syncer::SyncService::TransportState::ACTIVE);
-      service->SetPassphraseRequired(false);
-      service->SetDisableReasons(syncer::SyncService::DISABLE_REASON_NONE);
-      service->SetDetailedSyncStatus(false, syncer::SyncEngine::Status());
-
-      // This case will be run in platforms supporting mutation of the primary
-      // account (i.e., not ChromeOS) only, so we can assume mutator != nullptr.
-      auto* primary_account_mutator =
-          identity_manager->GetPrimaryAccountMutator();
-      DCHECK(primary_account_mutator);
-
-      // Starting the auth process and not completing it will make the mutator
-      // report "auth in progress" when checked from the test later on.
-      primary_account_mutator
-          ->LegacyStartSigninWithRefreshTokenForPrimaryAccount(
-              kRefreshToken, kTestGaiaId, kTestUser,
-              base::BindOnce([](const std::string& refresh_token) {
-                // Check that the token is properly passed along.
-                EXPECT_EQ(kRefreshToken, refresh_token);
-              }));
-      return;
-    }
     case STATUS_CASE_AUTH_ERROR: {
       service->SetFirstSetupComplete(true);
       service->SetTransportState(syncer::SyncService::TransportState::ACTIVE);
@@ -174,8 +146,6 @@
       return sync_ui_util::NO_ACTION;
     case STATUS_CASE_SETUP_ERROR:
       return sync_ui_util::REAUTHENTICATE;
-    case STATUS_CASE_AUTHENTICATING:
-      return sync_ui_util::NO_ACTION;
     case STATUS_CASE_AUTH_ERROR:
       return sync_ui_util::REAUTHENTICATE;
     case STATUS_CASE_PROTOCOL_ERROR:
@@ -226,13 +196,6 @@
     identity::IdentityManager* identity_manager =
         environment->identity_manager();
 
-    // We can't check the "Authenticating" case in platforms that don't support
-    // mutation of the primary account (e.g. ChromeOS), so skip those cases.
-    if (idx == STATUS_CASE_AUTHENTICATING &&
-        !identity_manager->GetPrimaryAccountMutator()) {
-      continue;
-    }
-
     // Need a primary account signed in before calling GetDistinctCase().
     environment->MakePrimaryAccountAvailable(kTestUser);
 
diff --git a/chrome/browser/task_manager/providers/arc/arc_process_task.cc b/chrome/browser/task_manager/providers/arc/arc_process_task.cc
index ec12563..a7e4611 100644
--- a/chrome/browser/task_manager/providers/arc/arc_process_task.cc
+++ b/chrome/browser/task_manager/providers/arc/arc_process_task.cc
@@ -12,6 +12,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
+#include "components/arc/arc_util.h"
 #include "components/arc/common/process.mojom.h"
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -130,6 +131,10 @@
   return !arc_process_.IsPersistent();
 }
 
+bool ArcProcessTask::IsRunningInVM() const {
+  return arc::IsArcVmEnabled();
+}
+
 void ArcProcessTask::Kill() {
   auto* process_instance = ARC_GET_INSTANCE_FOR_METHOD(
       arc::ArcServiceManager::Get()->arc_bridge_service()->process(),
diff --git a/chrome/browser/task_manager/providers/arc/arc_process_task.h b/chrome/browser/task_manager/providers/arc/arc_process_task.h
index adebbdf9..c4b4d6a 100644
--- a/chrome/browser/task_manager/providers/arc/arc_process_task.h
+++ b/chrome/browser/task_manager/providers/arc/arc_process_task.h
@@ -33,6 +33,7 @@
   int GetChildProcessUniqueID() const override;
   bool IsKillable() override;
   void Kill() override;
+  bool IsRunningInVM() const override;
 
   // arc::ConnectionObserver<arc::mojom::IntentHelperInstance>:
   void OnConnectionReady() override;
diff --git a/chrome/browser/task_manager/providers/task.cc b/chrome/browser/task_manager/providers/task.cc
index 9ff36e9c..74f3cb2 100644
--- a/chrome/browser/task_manager/providers/task.cc
+++ b/chrome/browser/task_manager/providers/task.cc
@@ -191,6 +191,10 @@
   return -1;
 }
 
+bool Task::IsRunningInVM() const {
+  return false;
+}
+
 // static
 gfx::ImageSkia* Task::FetchIcon(int id, gfx::ImageSkia** result_image) {
   if (!*result_image && ui::ResourceBundle::HasSharedInstance()) {
diff --git a/chrome/browser/task_manager/providers/task.h b/chrome/browser/task_manager/providers/task.h
index 2eefac6..6a6f9e6 100644
--- a/chrome/browser/task_manager/providers/task.h
+++ b/chrome/browser/task_manager/providers/task.h
@@ -154,6 +154,9 @@
   // Returns the keep-alive counter if the Task is an event page, -1 otherwise.
   virtual int GetKeepaliveCount() const;
 
+  // Returns true if the task is running inside a VM.
+  virtual bool IsRunningInVM() const;
+
   int64_t task_id() const { return task_id_; }
 
   // Returns the instantaneous rate, in bytes per second, of network usage
diff --git a/chrome/browser/task_manager/sampling/task_group.cc b/chrome/browser/task_manager/sampling/task_group.cc
index 43f4fed..9407ea1 100644
--- a/chrome/browser/task_manager/sampling/task_group.cc
+++ b/chrome/browser/task_manager/sampling/task_group.cc
@@ -83,11 +83,13 @@
 TaskGroup::TaskGroup(
     base::ProcessHandle proc_handle,
     base::ProcessId proc_id,
+    bool is_running_in_vm,
     const base::Closure& on_background_calculations_done,
     const scoped_refptr<SharedSampler>& shared_sampler,
     const scoped_refptr<base::SequencedTaskRunner>& blocking_pool_runner)
     : process_handle_(proc_handle),
       process_id_(proc_id),
+      is_running_in_vm_(is_running_in_vm),
       on_background_calculations_done_(on_background_calculations_done),
       worker_thread_sampler_(nullptr),
       shared_sampler_(shared_sampler),
@@ -119,7 +121,7 @@
       gpu_memory_has_duplicates_(false),
       is_backgrounded_(false),
       weak_ptr_factory_(this) {
-  if (process_id_ != base::kNullProcessId) {
+  if (process_id_ != base::kNullProcessId && !is_running_in_vm_) {
     worker_thread_sampler_ = base::MakeRefCounted<TaskGroupSampler>(
         base::Process::Open(process_id_), blocking_pool_runner,
         base::Bind(&TaskGroup::OnCpuRefreshDone,
@@ -164,6 +166,9 @@
                         int64_t refresh_flags) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(!empty());
+  if (is_running_in_vm_)
+    refresh_flags &= ~kUnsupportedVMRefreshFlags;
+
   expected_on_bg_done_flags_ = refresh_flags & kBackgroundRefreshTypesMask;
   // If a refresh type was recently disabled, we need to account for that too.
   current_on_bg_done_flags_ &= expected_on_bg_done_flags_;
diff --git a/chrome/browser/task_manager/sampling/task_group.h b/chrome/browser/task_manager/sampling/task_group.h
index 9ada18b..e6a7de8 100644
--- a/chrome/browser/task_manager/sampling/task_group.h
+++ b/chrome/browser/task_manager/sampling/task_group.h
@@ -29,6 +29,18 @@
 
 namespace task_manager {
 
+// A mask for refresh flags that are not supported by VM tasks.
+constexpr int kUnsupportedVMRefreshFlags =
+    REFRESH_TYPE_CPU | REFRESH_TYPE_SWAPPED_MEM | REFRESH_TYPE_GPU_MEMORY |
+    REFRESH_TYPE_V8_MEMORY | REFRESH_TYPE_SQLITE_MEMORY |
+    REFRESH_TYPE_WEBCACHE_STATS | REFRESH_TYPE_NETWORK_USAGE |
+    REFRESH_TYPE_NACL | REFRESH_TYPE_IDLE_WAKEUPS | REFRESH_TYPE_HANDLES |
+    REFRESH_TYPE_START_TIME | REFRESH_TYPE_CPU_TIME | REFRESH_TYPE_PRIORITY |
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+    REFRESH_TYPE_FD_COUNT |
+#endif
+    REFRESH_TYPE_HARD_FAULTS;
+
 class SharedSampler;
 
 // Defines a group of tasks tracked by the task manager which belong to the same
@@ -38,6 +50,7 @@
   TaskGroup(
       base::ProcessHandle proc_handle,
       base::ProcessId proc_id,
+      bool is_running_in_vm,
       const base::Closure& on_background_calculations_done,
       const scoped_refptr<SharedSampler>& shared_sampler,
       const scoped_refptr<base::SequencedTaskRunner>& blocking_pool_runner);
@@ -143,6 +156,7 @@
   // The process' handle and ID.
   base::ProcessHandle process_handle_;
   base::ProcessId process_id_;
+  bool is_running_in_vm_;
 
   // This is a callback into the TaskManagerImpl to inform it that the
   // background calculations for this TaskGroup has finished.
diff --git a/chrome/browser/task_manager/sampling/task_group_unittest.cc b/chrome/browser/task_manager/sampling/task_group_unittest.cc
index 5c0fa62..c8b20d9 100644
--- a/chrome/browser/task_manager/sampling/task_group_unittest.cc
+++ b/chrome/browser/task_manager/sampling/task_group_unittest.cc
@@ -28,13 +28,14 @@
 
 class FakeTask : public Task {
  public:
-  FakeTask(base::ProcessId process_id, Type type)
+  FakeTask(base::ProcessId process_id, Type type, bool is_running_in_vm)
       : Task(base::string16(),
              "FakeTask",
              nullptr,
              base::kNullProcessHandle,
              process_id),
-        type_(type) {}
+        type_(type),
+        is_running_in_vm_(is_running_in_vm) {}
 
   Type GetType() const override { return type_; }
 
@@ -44,8 +45,11 @@
 
   SessionID GetTabId() const override { return SessionID::InvalidValue(); }
 
+  bool IsRunningInVM() const override { return is_running_in_vm_; }
+
  private:
   Type type_;
+  bool is_running_in_vm_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeTask);
 };
@@ -57,17 +61,7 @@
   TaskGroupTest()
       : io_task_runner_(base::CreateSingleThreadTaskRunnerWithTraits(
             {content::BrowserThread::IO})),
-        run_loop_(std::make_unique<base::RunLoop>()),
-        task_group_(base::Process::Current().Handle(),
-                    base::Process::Current().Pid(),
-                    base::Bind(&TaskGroupTest::OnBackgroundCalculationsDone,
-                               base::Unretained(this)),
-                    new SharedSampler(io_task_runner_),
-                    io_task_runner_),
-        fake_task_(base::Process::Current().Pid(), Task::UNKNOWN) {
-    // Refresh() is only valid on non-empty TaskGroups, so add a fake Task.
-    task_group_.AddTask(&fake_task_);
-  }
+        run_loop_(std::make_unique<base::RunLoop>()) {}
 
  protected:
   void OnBackgroundCalculationsDone() {
@@ -76,11 +70,24 @@
     run_loop_->QuitWhenIdle();
   }
 
+  void CreateTaskGroup(bool is_running_in_vm) {
+    task_group_ = std::make_unique<TaskGroup>(
+        base::Process::Current().Handle(), base::Process::Current().Pid(),
+        is_running_in_vm,
+        base::Bind(&TaskGroupTest::OnBackgroundCalculationsDone,
+                   base::Unretained(this)),
+        new SharedSampler(io_task_runner_), io_task_runner_);
+    // Refresh() is only valid on non-empty TaskGroups, so add a fake Task.
+    fake_task_ = std::make_unique<FakeTask>(base::Process::Current().Pid(),
+                                            Task::UNKNOWN, is_running_in_vm);
+    task_group_->AddTask(fake_task_.get());
+  }
+
   content::TestBrowserThreadBundle browser_threads_;
   scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
   std::unique_ptr<base::RunLoop> run_loop_;
-  TaskGroup task_group_;
-  FakeTask fake_task_;
+  std::unique_ptr<TaskGroup> task_group_;
+  std::unique_ptr<FakeTask> fake_task_;
   bool background_refresh_complete_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TaskGroupTest);
@@ -90,26 +97,29 @@
 // refresh trivially completes, without crashing or leaving things in a weird
 // state.
 TEST_F(TaskGroupTest, NullRefresh) {
-  task_group_.Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(), 0);
-  EXPECT_TRUE(task_group_.AreBackgroundCalculationsDone());
+  CreateTaskGroup(false);
+  task_group_->Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(), 0);
+  EXPECT_TRUE(task_group_->AreBackgroundCalculationsDone());
   EXPECT_FALSE(background_refresh_complete_);
 }
 
 // Ensure that refreshing an empty TaskGroup causes a DCHECK (if enabled).
 TEST_F(TaskGroupTest, RefreshZeroTasksDeathTest) {
+  CreateTaskGroup(false);
   // Remove the fake Task from the group.
-  task_group_.RemoveTask(&fake_task_);
+  task_group_->RemoveTask(fake_task_.get());
 
   EXPECT_DCHECK_DEATH(
-      task_group_.Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(), 0));
+      task_group_->Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(), 0));
 }
 
 // Verify that Refresh() for a field which can be refreshed synchronously
 // completes immediately, without leaving any background calculations pending.
 TEST_F(TaskGroupTest, SyncRefresh) {
-  task_group_.Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(),
-                      REFRESH_TYPE_NETWORK_USAGE);
-  EXPECT_TRUE(task_group_.AreBackgroundCalculationsDone());
+  CreateTaskGroup(false);
+  task_group_->Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(),
+                       REFRESH_TYPE_NETWORK_USAGE);
+  EXPECT_TRUE(task_group_->AreBackgroundCalculationsDone());
   EXPECT_FALSE(background_refresh_complete_);
 }
 
@@ -117,14 +127,15 @@
 // work (e.g. on another thread) to complete. Cpu is such a field, so verify
 // that it is correctly reported as requiring background calculations.
 TEST_F(TaskGroupTest, AsyncRefresh) {
-  task_group_.Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(),
-                      REFRESH_TYPE_CPU);
-  EXPECT_FALSE(task_group_.AreBackgroundCalculationsDone());
+  CreateTaskGroup(false);
+  task_group_->Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(),
+                       REFRESH_TYPE_CPU);
+  EXPECT_FALSE(task_group_->AreBackgroundCalculationsDone());
 
   ASSERT_FALSE(background_refresh_complete_);
   run_loop_->Run();
 
-  EXPECT_TRUE(task_group_.AreBackgroundCalculationsDone());
+  EXPECT_TRUE(task_group_->AreBackgroundCalculationsDone());
   EXPECT_TRUE(background_refresh_complete_);
 }
 
@@ -133,29 +144,32 @@
 // and via asynchronous refresh on others, so we just test that that field
 // requires background calculations, similarly to the AsyncRefresh test above.
 TEST_F(TaskGroupTest, SharedAsyncRefresh) {
-  task_group_.Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(),
-                      REFRESH_TYPE_IDLE_WAKEUPS);
-  EXPECT_FALSE(task_group_.AreBackgroundCalculationsDone());
+  CreateTaskGroup(false);
+  task_group_->Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(),
+                       REFRESH_TYPE_IDLE_WAKEUPS);
+  EXPECT_FALSE(task_group_->AreBackgroundCalculationsDone());
 
   ASSERT_FALSE(background_refresh_complete_);
   run_loop_->Run();
 
   EXPECT_TRUE(background_refresh_complete_);
 
-  EXPECT_TRUE(task_group_.AreBackgroundCalculationsDone());
+  EXPECT_TRUE(task_group_->AreBackgroundCalculationsDone());
 }
 
 // Ensure that if NaCl is enabled then calling Refresh with a NaCl Task active
 // results in asynchronous completion. Also verifies that if NaCl is disabled
 // then completion is synchronous.
 TEST_F(TaskGroupTest, NaclRefreshWithTask) {
-  FakeTask fake_task(base::Process::Current().Pid(), Task::NACL);
-  task_group_.AddTask(&fake_task);
+  CreateTaskGroup(false);
+  FakeTask fake_task(base::Process::Current().Pid(), Task::NACL,
+                     false /* is_running_in_vm */);
+  task_group_->AddTask(&fake_task);
 
-  task_group_.Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(),
-                      REFRESH_TYPE_NACL);
+  task_group_->Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(),
+                       REFRESH_TYPE_NACL);
 #if BUILDFLAG(ENABLE_NACL)
-  EXPECT_FALSE(task_group_.AreBackgroundCalculationsDone());
+  EXPECT_FALSE(task_group_->AreBackgroundCalculationsDone());
 
   ASSERT_FALSE(background_refresh_complete_);
   run_loop_->Run();
@@ -163,13 +177,15 @@
   EXPECT_TRUE(background_refresh_complete_);
 #endif  // BUILDFLAG(ENABLE_NACL)
 
-  EXPECT_TRUE(task_group_.AreBackgroundCalculationsDone());
+  EXPECT_TRUE(task_group_->AreBackgroundCalculationsDone());
 }
 
 // Test the task has correct network usage rate when zero bytes read and sent.
 TEST_F(TaskGroupTest, NetworkBytesSentReadZero) {
+  CreateTaskGroup(false);
   const int zero_bytes = 0;
-  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER,
+                     false /* is_running_in_vm */);
   fake_task.OnNetworkBytesRead(zero_bytes);
   fake_task.Refresh(base::TimeDelta::FromSeconds(1),
                     REFRESH_TYPE_NETWORK_USAGE);
@@ -182,8 +198,10 @@
 
 // Test the task has correct network usage rate when only having read bytes.
 TEST_F(TaskGroupTest, NetworkBytesRead) {
+  CreateTaskGroup(false);
   const int read_bytes = 1024;
-  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER,
+                     false /* is_running_in_vm */);
   fake_task.OnNetworkBytesRead(read_bytes);
   EXPECT_EQ(0, fake_task.network_usage_rate());
   EXPECT_EQ(read_bytes, fake_task.cumulative_network_usage());
@@ -195,8 +213,10 @@
 
 // Test the task has correct network usage rate when only having sent bytes.
 TEST_F(TaskGroupTest, NetworkBytesSent) {
+  CreateTaskGroup(false);
   const int sent_bytes = 1023;
-  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER,
+                     false /* is_running_in_vm */);
   fake_task.OnNetworkBytesSent(sent_bytes);
   EXPECT_EQ(0, fake_task.network_usage_rate());
   EXPECT_EQ(sent_bytes, fake_task.cumulative_network_usage());
@@ -209,9 +229,11 @@
 // Test the task has correct network usage rate when only having read bytes and
 // having a non 1s refresh time.
 TEST_F(TaskGroupTest, NetworkBytesRead2SecRefresh) {
+  CreateTaskGroup(false);
   const int refresh_secs = 2;
   const int read_bytes = 1024 * refresh_secs;  // for integer division
-  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER,
+                     false /* is_running_in_vm */);
   fake_task.OnNetworkBytesRead(read_bytes);
   EXPECT_EQ(0, fake_task.network_usage_rate());
   EXPECT_EQ(read_bytes, fake_task.cumulative_network_usage());
@@ -224,9 +246,11 @@
 // Test the task has correct network usage rate when only having sent bytes and
 // having a non 1s refresh time.
 TEST_F(TaskGroupTest, NetworkBytesSent2SecRefresh) {
+  CreateTaskGroup(false);
   const int refresh_secs = 2;
   const int sent_bytes = 1023 * refresh_secs;  // for integer division
-  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER,
+                     false /* is_running_in_vm */);
   fake_task.OnNetworkBytesSent(sent_bytes);
   EXPECT_EQ(0, fake_task.network_usage_rate());
   EXPECT_EQ(sent_bytes, fake_task.cumulative_network_usage());
@@ -238,9 +262,11 @@
 
 // Tests the task has correct usage on receiving and then sending bytes.
 TEST_F(TaskGroupTest, NetworkBytesReadThenSent) {
+  CreateTaskGroup(false);
   const int read_bytes = 124;
   const int sent_bytes = 1027;
-  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER,
+                     false /* is_running_in_vm */);
   fake_task.OnNetworkBytesRead(read_bytes);
   EXPECT_EQ(read_bytes, fake_task.cumulative_network_usage());
   fake_task.OnNetworkBytesSent(sent_bytes);
@@ -252,9 +278,11 @@
 
 // Tests the task has correct usage rate on sending and then receiving bytes.
 TEST_F(TaskGroupTest, NetworkBytesSentThenRead) {
+  CreateTaskGroup(false);
   const int read_bytes = 1025;
   const int sent_bytes = 10;
-  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER,
+                     false /* is_running_in_vm */);
   fake_task.OnNetworkBytesSent(sent_bytes);
   fake_task.OnNetworkBytesRead(read_bytes);
   fake_task.Refresh(base::TimeDelta::FromSeconds(1),
@@ -265,8 +293,10 @@
 // Tests that the network usage rate goes to 0 after reading bytes then a
 // refresh with no traffic and that cumulative is still correct.
 TEST_F(TaskGroupTest, NetworkBytesReadRefreshNone) {
+  CreateTaskGroup(false);
   const int read_bytes = 1024;
-  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER,
+                     false /* is_running_in_vm */);
   fake_task.OnNetworkBytesRead(read_bytes);
   fake_task.Refresh(base::TimeDelta::FromSeconds(1),
                     REFRESH_TYPE_NETWORK_USAGE);
@@ -280,8 +310,10 @@
 // Tests that the network usage rate goes to 0 after sending bytes then a
 // refresh with no traffic and that cumulative is still correct.
 TEST_F(TaskGroupTest, NetworkBytesSentRefreshNone) {
+  CreateTaskGroup(false);
   const int sent_bytes = 1024;
-  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER,
+                     false /* is_running_in_vm */);
   fake_task.OnNetworkBytesSent(sent_bytes);
   fake_task.Refresh(base::TimeDelta::FromSeconds(1),
                     REFRESH_TYPE_NETWORK_USAGE);
@@ -295,10 +327,12 @@
 // Tests that the network usage rate goes to 0 after a refresh with no traffic
 // and that cumulative is still correct.
 TEST_F(TaskGroupTest, NetworkBytesTransferredRefreshNone) {
+  CreateTaskGroup(false);
   const int read_bytes = 1024;
   const int sent_bytes = 1;
   const int number_of_cycles = 2;
-  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER,
+                     false /* is_running_in_vm */);
   for (int i = 0; i < number_of_cycles; i++) {
     fake_task.OnNetworkBytesRead(read_bytes);
     fake_task.Refresh(base::TimeDelta::FromSeconds(1),
@@ -318,37 +352,42 @@
 // Tests that 2 tasks in 1 task group that both read bytes have correct usage
 // rates and correct cumulative network usage.
 TEST_F(TaskGroupTest, NetworkBytesReadAsGroup) {
+  CreateTaskGroup(false);
   const int read_bytes1 = 1024;
   const int read_bytes2 = 789;
   const int number_of_cycles = 2;
-  FakeTask fake_task1(base::Process::Current().Pid(), Task::RENDERER);
-  FakeTask fake_task2(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task1(base::Process::Current().Pid(), Task::RENDERER,
+                      false /* is_running_in_vm */);
+  FakeTask fake_task2(base::Process::Current().Pid(), Task::RENDERER,
+                      false /* is_running_in_vm */);
 
-  task_group_.AddTask(&fake_task1);
-  task_group_.AddTask(&fake_task2);
+  task_group_->AddTask(&fake_task1);
+  task_group_->AddTask(&fake_task2);
 
   for (int i = 0; i < number_of_cycles; i++) {
     fake_task1.OnNetworkBytesRead(read_bytes1);
     fake_task2.OnNetworkBytesRead(read_bytes2);
-    task_group_.Refresh(gpu::VideoMemoryUsageStats(),
-                        base::TimeDelta::FromSeconds(1),
-                        REFRESH_TYPE_NETWORK_USAGE);
+    task_group_->Refresh(gpu::VideoMemoryUsageStats(),
+                         base::TimeDelta::FromSeconds(1),
+                         REFRESH_TYPE_NETWORK_USAGE);
     EXPECT_EQ(read_bytes1 + read_bytes2,
-              task_group_.per_process_network_usage_rate());
+              task_group_->per_process_network_usage_rate());
   }
 
   EXPECT_EQ((read_bytes1 + read_bytes2) * number_of_cycles,
-            task_group_.cumulative_per_process_network_usage());
+            task_group_->cumulative_per_process_network_usage());
 }
 
 // Tests that the network usage rate does not get affected until a refresh is
 // called and that the cumulative is as up to date as possible.
 TEST_F(TaskGroupTest, NetworkBytesTransferredRefreshOutOfOrder) {
+  CreateTaskGroup(false);
   const int read_bytes = 1024;
   const int sent_bytes = 1;
   const int number_of_cycles = 4;
   int number_of_bytes_transferred = 0;
-  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task(base::Process::Current().Pid(), Task::RENDERER,
+                     false /* is_running_in_vm */);
   for (int i = 0; i < number_of_cycles; i++) {
     fake_task.OnNetworkBytesRead(read_bytes * i);
     number_of_bytes_transferred += read_bytes * i;
@@ -376,151 +415,178 @@
 // Tests that 2 tasks in 1 task group that both sent bytes have correct usage
 // rates and correct cumulative network usage.
 TEST_F(TaskGroupTest, NetworkBytesSentAsGroup) {
+  CreateTaskGroup(false);
   const int sent_bytes1 = 1123;
   const int sent_bytes2 = 778;
-  FakeTask fake_task1(base::Process::Current().Pid(), Task::RENDERER);
-  FakeTask fake_task2(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task1(base::Process::Current().Pid(), Task::RENDERER,
+                      false /* is_running_in_vm */);
+  FakeTask fake_task2(base::Process::Current().Pid(), Task::RENDERER,
+                      false /* is_running_in_vm */);
 
-  task_group_.AddTask(&fake_task1);
-  task_group_.AddTask(&fake_task2);
+  task_group_->AddTask(&fake_task1);
+  task_group_->AddTask(&fake_task2);
 
   fake_task1.OnNetworkBytesSent(sent_bytes1);
   fake_task2.OnNetworkBytesSent(sent_bytes2);
-  task_group_.Refresh(gpu::VideoMemoryUsageStats(),
-                      base::TimeDelta::FromSeconds(1),
-                      REFRESH_TYPE_NETWORK_USAGE);
+  task_group_->Refresh(gpu::VideoMemoryUsageStats(),
+                       base::TimeDelta::FromSeconds(1),
+                       REFRESH_TYPE_NETWORK_USAGE);
   EXPECT_EQ(sent_bytes1 + sent_bytes2,
-            task_group_.per_process_network_usage_rate());
+            task_group_->per_process_network_usage_rate());
 
   fake_task1.OnNetworkBytesSent(sent_bytes1);
   fake_task2.OnNetworkBytesSent(sent_bytes2);
-  task_group_.Refresh(gpu::VideoMemoryUsageStats(),
-                      base::TimeDelta::FromSeconds(1),
-                      REFRESH_TYPE_NETWORK_USAGE);
+  task_group_->Refresh(gpu::VideoMemoryUsageStats(),
+                       base::TimeDelta::FromSeconds(1),
+                       REFRESH_TYPE_NETWORK_USAGE);
 
   EXPECT_EQ((sent_bytes1 + sent_bytes2) * 2,
-            task_group_.cumulative_per_process_network_usage());
+            task_group_->cumulative_per_process_network_usage());
 }
 
 // Tests that 2 tasks in 1  task group that have one sending and one reading
 // have correct usage rates for the group and correct cumulative network usage.
 TEST_F(TaskGroupTest, NetworkBytesTransferredAsGroup) {
+  CreateTaskGroup(false);
   const int sent_bytes = 1023;
   const int read_bytes = 678;
   const int number_of_cycles = 2;
-  FakeTask fake_task1(base::Process::Current().Pid(), Task::RENDERER);
-  FakeTask fake_task2(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task1(base::Process::Current().Pid(), Task::RENDERER,
+                      false /* is_running_in_vm */);
+  FakeTask fake_task2(base::Process::Current().Pid(), Task::RENDERER,
+                      false /* is_running_in_vm */);
 
-  task_group_.AddTask(&fake_task1);
-  task_group_.AddTask(&fake_task2);
+  task_group_->AddTask(&fake_task1);
+  task_group_->AddTask(&fake_task2);
   for (int i = 0; i < number_of_cycles; i++) {
     fake_task1.OnNetworkBytesSent(sent_bytes);
     fake_task2.OnNetworkBytesRead(read_bytes);
-    task_group_.Refresh(gpu::VideoMemoryUsageStats(),
-                        base::TimeDelta::FromSeconds(1),
-                        REFRESH_TYPE_NETWORK_USAGE);
+    task_group_->Refresh(gpu::VideoMemoryUsageStats(),
+                         base::TimeDelta::FromSeconds(1),
+                         REFRESH_TYPE_NETWORK_USAGE);
     EXPECT_EQ(sent_bytes + read_bytes,
-              task_group_.per_process_network_usage_rate());
+              task_group_->per_process_network_usage_rate());
   }
 
   EXPECT_EQ((read_bytes + sent_bytes) * number_of_cycles,
-            task_group_.cumulative_per_process_network_usage());
+            task_group_->cumulative_per_process_network_usage());
 }
 
 // Tests that after two tasks in a task group read bytes that a refresh will
 // zero out network usage rate while maintaining the correct cumulative network
 // usage.
 TEST_F(TaskGroupTest, NetworkBytesReadAsGroupThenNone) {
+  CreateTaskGroup(false);
   const int read_bytes1 = 1013;
   const int read_bytes2 = 679;
   const int number_of_cycles = 2;
-  FakeTask fake_task1(base::Process::Current().Pid(), Task::RENDERER);
-  FakeTask fake_task2(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task1(base::Process::Current().Pid(), Task::RENDERER,
+                      false /* is_running_in_vm */);
+  FakeTask fake_task2(base::Process::Current().Pid(), Task::RENDERER,
+                      false /* is_running_in_vm */);
 
-  task_group_.AddTask(&fake_task1);
-  task_group_.AddTask(&fake_task2);
+  task_group_->AddTask(&fake_task1);
+  task_group_->AddTask(&fake_task2);
 
   for (int i = 0; i < number_of_cycles; i++) {
     fake_task1.OnNetworkBytesRead(read_bytes1);
     fake_task2.OnNetworkBytesRead(read_bytes2);
-    task_group_.Refresh(gpu::VideoMemoryUsageStats(),
-                        base::TimeDelta::FromSeconds(1),
-                        REFRESH_TYPE_NETWORK_USAGE);
+    task_group_->Refresh(gpu::VideoMemoryUsageStats(),
+                         base::TimeDelta::FromSeconds(1),
+                         REFRESH_TYPE_NETWORK_USAGE);
     EXPECT_EQ(read_bytes1 + read_bytes2,
-              task_group_.per_process_network_usage_rate());
+              task_group_->per_process_network_usage_rate());
   }
-  task_group_.Refresh(gpu::VideoMemoryUsageStats(),
-                      base::TimeDelta::FromSeconds(1),
-                      REFRESH_TYPE_NETWORK_USAGE);
-  EXPECT_EQ(0, task_group_.per_process_network_usage_rate());
+  task_group_->Refresh(gpu::VideoMemoryUsageStats(),
+                       base::TimeDelta::FromSeconds(1),
+                       REFRESH_TYPE_NETWORK_USAGE);
+  EXPECT_EQ(0, task_group_->per_process_network_usage_rate());
   EXPECT_EQ((read_bytes1 + read_bytes2) * number_of_cycles,
-            task_group_.cumulative_per_process_network_usage());
+            task_group_->cumulative_per_process_network_usage());
 }
 
 // Tests that after two tasks in a task group send bytes that a refresh will
 // zero out network usage rate while maintaining the correct cumulative network
 // usage.
 TEST_F(TaskGroupTest, NetworkBytesSentAsGroupThenNone) {
+  CreateTaskGroup(false);
   const int sent_bytes1 = 1023;
   const int sent_bytes2 = 678;
   const int number_of_cycles = 2;
-  FakeTask fake_task1(base::Process::Current().Pid(), Task::RENDERER);
-  FakeTask fake_task2(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task1(base::Process::Current().Pid(), Task::RENDERER,
+                      false /* is_running_in_vm */);
+  FakeTask fake_task2(base::Process::Current().Pid(), Task::RENDERER,
+                      false /* is_running_in_vm */);
 
-  task_group_.AddTask(&fake_task1);
-  task_group_.AddTask(&fake_task2);
+  task_group_->AddTask(&fake_task1);
+  task_group_->AddTask(&fake_task2);
 
   for (int i = 0; i < number_of_cycles; i++) {
     fake_task1.OnNetworkBytesSent(sent_bytes1);
     fake_task2.OnNetworkBytesSent(sent_bytes2);
-    task_group_.Refresh(gpu::VideoMemoryUsageStats(),
-                        base::TimeDelta::FromSeconds(1),
-                        REFRESH_TYPE_NETWORK_USAGE);
+    task_group_->Refresh(gpu::VideoMemoryUsageStats(),
+                         base::TimeDelta::FromSeconds(1),
+                         REFRESH_TYPE_NETWORK_USAGE);
     EXPECT_EQ(sent_bytes1 + sent_bytes2,
-              task_group_.per_process_network_usage_rate());
+              task_group_->per_process_network_usage_rate());
   }
-  task_group_.Refresh(gpu::VideoMemoryUsageStats(),
-                      base::TimeDelta::FromSeconds(1),
-                      REFRESH_TYPE_NETWORK_USAGE);
-  EXPECT_EQ(0, task_group_.per_process_network_usage_rate());
+  task_group_->Refresh(gpu::VideoMemoryUsageStats(),
+                       base::TimeDelta::FromSeconds(1),
+                       REFRESH_TYPE_NETWORK_USAGE);
+  EXPECT_EQ(0, task_group_->per_process_network_usage_rate());
   EXPECT_EQ((sent_bytes1 + sent_bytes2) * number_of_cycles,
-            task_group_.cumulative_per_process_network_usage());
+            task_group_->cumulative_per_process_network_usage());
 }
 
 // Tests that after two tasks in a task group transferred bytes that a refresh
 // will zero out network usage rate while maintaining the correct cumulative
 // network usage.
 TEST_F(TaskGroupTest, NetworkBytesTransferredAsGroupThenNone) {
+  CreateTaskGroup(false);
   const int read_bytes = 321;
   const int sent_bytes = 987;
   const int number_of_cycles = 3;
-  FakeTask fake_task1(base::Process::Current().Pid(), Task::RENDERER);
-  FakeTask fake_task2(base::Process::Current().Pid(), Task::RENDERER);
+  FakeTask fake_task1(base::Process::Current().Pid(), Task::RENDERER,
+                      false /* is_running_in_vm */);
+  FakeTask fake_task2(base::Process::Current().Pid(), Task::RENDERER,
+                      false /* is_running_in_vm */);
 
-  task_group_.AddTask(&fake_task1);
-  task_group_.AddTask(&fake_task2);
+  task_group_->AddTask(&fake_task1);
+  task_group_->AddTask(&fake_task2);
 
   for (int i = 0; i < number_of_cycles; i++) {
     fake_task1.OnNetworkBytesRead(read_bytes);
     fake_task2.OnNetworkBytesSent(sent_bytes);
-    task_group_.Refresh(gpu::VideoMemoryUsageStats(),
-                        base::TimeDelta::FromSeconds(1),
-                        REFRESH_TYPE_NETWORK_USAGE);
+    task_group_->Refresh(gpu::VideoMemoryUsageStats(),
+                         base::TimeDelta::FromSeconds(1),
+                         REFRESH_TYPE_NETWORK_USAGE);
     EXPECT_EQ(read_bytes + sent_bytes,
-              task_group_.per_process_network_usage_rate());
+              task_group_->per_process_network_usage_rate());
   }
-  task_group_.Refresh(gpu::VideoMemoryUsageStats(),
-                      base::TimeDelta::FromSeconds(1),
-                      REFRESH_TYPE_NETWORK_USAGE);
-  EXPECT_EQ(0, task_group_.per_process_network_usage_rate());
+  task_group_->Refresh(gpu::VideoMemoryUsageStats(),
+                       base::TimeDelta::FromSeconds(1),
+                       REFRESH_TYPE_NETWORK_USAGE);
+  EXPECT_EQ(0, task_group_->per_process_network_usage_rate());
   EXPECT_EQ((read_bytes + sent_bytes) * number_of_cycles,
-            task_group_.cumulative_per_process_network_usage());
+            task_group_->cumulative_per_process_network_usage());
 }
 
 // Test the task can't be killed with a PID of base::kNullProcessId.
 TEST_F(TaskGroupTest, TaskWithPidZero) {
-  FakeTask fake_task(base::kNullProcessId, Task::RENDERER);
+  CreateTaskGroup(false);
+  FakeTask fake_task(base::kNullProcessId, Task::RENDERER,
+                     false /* is_running_in_vm */);
   EXPECT_FALSE(fake_task.IsKillable());
 }
 
+// Verify that calling TaskGroup::Refresh() on a VM task group with no supported
+// refresh flags trivially completes.
+TEST_F(TaskGroupTest, UnsupportedVMRefreshFlags) {
+  CreateTaskGroup(true);
+  task_group_->Refresh(gpu::VideoMemoryUsageStats(), base::TimeDelta(),
+                       task_manager::kUnsupportedVMRefreshFlags);
+  EXPECT_TRUE(task_group_->AreBackgroundCalculationsDone());
+  EXPECT_FALSE(background_refresh_complete_);
+}
+
 }  // namespace task_manager
diff --git a/chrome/browser/task_manager/sampling/task_manager_impl.cc b/chrome/browser/task_manager/sampling/task_manager_impl.cc
index 1c50a9b9..6af3d41b7 100644
--- a/chrome/browser/task_manager/sampling/task_manager_impl.cc
+++ b/chrome/browser/task_manager/sampling/task_manager_impl.cc
@@ -335,7 +335,10 @@
                              b->task_id());
     };
 
-    const size_t num_groups = task_groups_by_proc_id_.size();
+    const size_t num_groups =
+        task_groups_by_proc_id_.size() + arc_vm_task_groups_by_proc_id_.size();
+
+    // |task_groups_by_task_id_| contains all tasks, both VM and non-VM.
     const size_t num_tasks = task_groups_by_task_id_.size();
 
     // Populate |tasks_to_visit| with one task from each group.
@@ -359,6 +362,13 @@
       }
     }
 
+    for (const auto& groups_pair : arc_vm_task_groups_by_proc_id_) {
+      const std::vector<Task*>& tasks = groups_pair.second->tasks();
+      Task* group_task =
+          *std::min_element(tasks.begin(), tasks.end(), comparator);
+      tasks_to_visit.push_back(group_task);
+    }
+
     // Now sort |tasks_to_visit| in reverse order (putting the browser process
     // at back()). We will treat it as a stack from now on.
     std::sort(tasks_to_visit.rbegin(), tasks_to_visit.rend(), comparator);
@@ -429,6 +439,10 @@
   return GetTaskGroupByTaskId(task_id)->num_tasks();
 }
 
+bool TaskManagerImpl::IsRunningInVM(TaskId task_id) const {
+  return GetTaskByTaskId(task_id)->IsRunningInVM();
+}
+
 TaskId TaskManagerImpl::GetTaskIdForWebContents(
     content::WebContents* web_contents) const {
   if (!web_contents)
@@ -445,10 +459,16 @@
 
   const base::ProcessId proc_id = task->process_id();
   const TaskId task_id = task->task_id();
+  const bool is_running_in_vm = task->IsRunningInVM();
 
-  std::unique_ptr<TaskGroup>& task_group = task_groups_by_proc_id_[proc_id];
+  TaskManagerImpl::PidToTaskGroupMap& task_group_map =
+      is_running_in_vm ? arc_vm_task_groups_by_proc_id_
+                       : task_groups_by_proc_id_;
+
+  std::unique_ptr<TaskGroup>& task_group = task_group_map[proc_id];
   if (!task_group) {
     task_group.reset(new TaskGroup(task->process_handle(), proc_id,
+                                   is_running_in_vm,
                                    on_background_data_ready_callback_,
                                    shared_sampler_, blocking_pool_runner_));
 #if defined(OS_CHROMEOS)
@@ -472,8 +492,13 @@
 
   const base::ProcessId proc_id = task->process_id();
   const TaskId task_id = task->task_id();
+  const bool is_running_in_vm = task->IsRunningInVM();
 
-  DCHECK(task_groups_by_proc_id_.count(proc_id));
+  TaskManagerImpl::PidToTaskGroupMap& task_group_map =
+      is_running_in_vm ? arc_vm_task_groups_by_proc_id_
+                       : task_groups_by_proc_id_;
+
+  DCHECK(task_group_map.count(proc_id));
 
   NotifyObserversOnTaskToBeRemoved(task_id);
 
@@ -482,7 +507,7 @@
   task_groups_by_task_id_.erase(task_id);
 
   if (task_group->empty())
-    task_groups_by_proc_id_.erase(proc_id);  // Deletes |task_group|.
+    task_group_map.erase(proc_id);  // Deletes |task_group|.
 
   // Invalidate the cached sorted IDs by clearing the list.
   sorted_task_ids_.clear();
@@ -612,6 +637,11 @@
                                enabled_resources_flags());
   }
 
+  for (auto& groups_itr : arc_vm_task_groups_by_proc_id_) {
+    groups_itr.second->Refresh(gpu_memory_stats_, GetCurrentRefreshTime(),
+                               enabled_resources_flags());
+  }
+
 #if defined(OS_CHROMEOS)
   if (TaskManagerObserver::IsResourceRefreshEnabled(
           REFRESH_TYPE_MEMORY_FOOTPRINT, enabled_resources_flags())) {
@@ -652,6 +682,7 @@
     provider->ClearObserver();
 
   task_groups_by_proc_id_.clear();
+  arc_vm_task_groups_by_proc_id_.clear();
   task_groups_by_task_id_.clear();
   sorted_task_ids_.clear();
 }
diff --git a/chrome/browser/task_manager/sampling/task_manager_impl.h b/chrome/browser/task_manager/sampling/task_manager_impl.h
index 7ef9949..9dc4cb3 100644
--- a/chrome/browser/task_manager/sampling/task_manager_impl.h
+++ b/chrome/browser/task_manager/sampling/task_manager_impl.h
@@ -89,6 +89,7 @@
   const TaskIdList& GetTaskIdsList() const override;
   TaskIdList GetIdsOfTasksSharingSameProcess(TaskId task_id) const override;
   size_t GetNumberOfTasksOnSameProcess(TaskId task_id) const override;
+  bool IsRunningInVM(TaskId task_id) const override;
   TaskId GetTaskIdForWebContents(
       content::WebContents* web_contents) const override;
 
@@ -109,6 +110,9 @@
       std::vector<network::mojom::NetworkUsagePtr> total_network_usages);
 
  private:
+  using PidToTaskGroupMap =
+      std::map<base::ProcessId, std::unique_ptr<TaskGroup>>;
+
   friend struct base::LazyInstanceTraitsBase<TaskManagerImpl>;
 
   TaskManagerImpl();
@@ -135,6 +139,7 @@
   bool UpdateTasksWithBytesTransferred(const BytesTransferredKey& key,
                                        const BytesTransferredParam& param);
 
+  PidToTaskGroupMap* GetVmPidToTaskGroupMap(Task::Type type);
   TaskGroup* GetTaskGroupByTaskId(TaskId task_id) const;
   Task* GetTaskByTaskId(TaskId task_id) const;
 
@@ -145,7 +150,12 @@
   const base::Closure on_background_data_ready_callback_;
 
   // Map TaskGroups by the IDs of the processes they represent.
-  std::map<base::ProcessId, std::unique_ptr<TaskGroup>> task_groups_by_proc_id_;
+  PidToTaskGroupMap task_groups_by_proc_id_;
+
+  // Map ARC VM PidToTaskGroupMaps by the task type. This should be separate
+  // from the non-VM map |task_groups_by_proc_id_| as there can be conflicting
+  // PIDs.
+  PidToTaskGroupMap arc_vm_task_groups_by_proc_id_;
 
   // Map each task by its ID to the TaskGroup on which it resides.
   // Keys are unique but values will have duplicates (i.e. multiple tasks
diff --git a/chrome/browser/task_manager/task_manager_interface.h b/chrome/browser/task_manager/task_manager_interface.h
index 1207c1b..73a108ff 100644
--- a/chrome/browser/task_manager/task_manager_interface.h
+++ b/chrome/browser/task_manager/task_manager_interface.h
@@ -234,6 +234,9 @@
   // the Task with |task_id| is running.
   virtual size_t GetNumberOfTasksOnSameProcess(TaskId task_id) const = 0;
 
+  // Returns true if the task is running inside a VM.
+  virtual bool IsRunningInVM(TaskId task_id) const = 0;
+
   // Returns the TaskId associated with the main task for |web_contents|.
   // Returns -1 if |web_contents| is nullptr or does not currently have an
   // associated Task.
diff --git a/chrome/browser/task_manager/test_task_manager.cc b/chrome/browser/task_manager/test_task_manager.cc
index a2e0cd80..7438cf7 100644
--- a/chrome/browser/task_manager/test_task_manager.cc
+++ b/chrome/browser/task_manager/test_task_manager.cc
@@ -177,6 +177,10 @@
   return 1;
 }
 
+bool TestTaskManager::IsRunningInVM(TaskId task_id) const {
+  return false;
+}
+
 TaskId TestTaskManager::GetTaskIdForWebContents(
     content::WebContents* web_contents) const {
   return -1;
diff --git a/chrome/browser/task_manager/test_task_manager.h b/chrome/browser/task_manager/test_task_manager.h
index 5097b225..788ee92 100644
--- a/chrome/browser/task_manager/test_task_manager.h
+++ b/chrome/browser/task_manager/test_task_manager.h
@@ -71,6 +71,7 @@
   const TaskIdList& GetTaskIdsList() const override;
   TaskIdList GetIdsOfTasksSharingSameProcess(TaskId task_id) const override;
   size_t GetNumberOfTasksOnSameProcess(TaskId task_id) const override;
+  bool IsRunningInVM(TaskId task_id) const override;
   TaskId GetTaskIdForWebContents(
       content::WebContents* web_contents) const override;
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index c0e2dab9..465e923 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2858,6 +2858,10 @@
       "views/update_recommended_message_box.h",
       "views/webauthn/authenticator_ble_pin_entry_sheet_view.cc",
       "views/webauthn/authenticator_ble_pin_entry_sheet_view.h",
+      "views/webauthn/authenticator_client_pin_entry_sheet_view.cc",
+      "views/webauthn/authenticator_client_pin_entry_sheet_view.h",
+      "views/webauthn/authenticator_client_pin_entry_view.cc",
+      "views/webauthn/authenticator_client_pin_entry_view.h",
       "views/webauthn/authenticator_request_dialog_view.cc",
       "views/webauthn/authenticator_request_dialog_view.h",
       "views/webauthn/authenticator_request_sheet_view.cc",
diff --git a/chrome/browser/ui/app_list/app_service_app_item.cc b/chrome/browser/ui/app_list/app_service_app_item.cc
index a9d9e32..e158a87 100644
--- a/chrome/browser/ui/app_list/app_service_app_item.cc
+++ b/chrome/browser/ui/app_list/app_service_app_item.cc
@@ -78,14 +78,8 @@
   }
 
   if (in_constructor || app_update.IconKeyChanged()) {
-    apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile());
-    if (proxy) {
-      proxy->LoadIcon(app_update.AppId(),
-                      apps::mojom::IconCompression::kUncompressed,
-                      app_list::AppListConfig::instance().grid_icon_dimension(),
-                      base::BindOnce(&AppServiceAppItem::OnLoadIcon,
-                                     weak_ptr_factory_.GetWeakPtr()));
-    }
+    constexpr bool allow_placeholder_icon = true;
+    CallLoadIcon(allow_placeholder_icon);
   }
 
   if (in_constructor || app_update.IsPlatformAppChanged()) {
@@ -131,10 +125,26 @@
   }
 }
 
+void AppServiceAppItem::CallLoadIcon(bool allow_placeholder_icon) {
+  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile());
+  if (proxy) {
+    proxy->LoadIcon(id(), apps::mojom::IconCompression::kUncompressed,
+                    app_list::AppListConfig::instance().grid_icon_dimension(),
+                    allow_placeholder_icon,
+                    base::BindOnce(&AppServiceAppItem::OnLoadIcon,
+                                   weak_ptr_factory_.GetWeakPtr()));
+  }
+}
+
 void AppServiceAppItem::OnLoadIcon(apps::mojom::IconValuePtr icon_value) {
   if (icon_value->icon_compression !=
       apps::mojom::IconCompression::kUncompressed) {
     return;
   }
   SetIcon(icon_value->uncompressed);
+
+  if (icon_value->is_placeholder_icon) {
+    constexpr bool allow_placeholder_icon = false;
+    CallLoadIcon(allow_placeholder_icon);
+  }
 }
diff --git a/chrome/browser/ui/app_list/app_service_app_item.h b/chrome/browser/ui/app_list/app_service_app_item.h
index 47be350..6aa1334 100644
--- a/chrome/browser/ui/app_list/app_service_app_item.h
+++ b/chrome/browser/ui/app_list/app_service_app_item.h
@@ -48,6 +48,8 @@
   void ExecuteLaunchCommand(int event_flags) override;
 
   void Launch(int event_flags, apps::mojom::LaunchSource launch_source);
+
+  void CallLoadIcon(bool allow_placeholder_icon);
   void OnLoadIcon(apps::mojom::IconValuePtr icon_value);
 
   apps::mojom::AppType app_type_;
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.cc b/chrome/browser/ui/app_list/search/app_service_app_result.cc
index 4239f0f..7df3d82 100644
--- a/chrome/browser/ui/app_list/search/app_service_app_result.cc
+++ b/chrome/browser/ui/app_list/search/app_service_app_result.cc
@@ -24,6 +24,7 @@
       is_platform_app_(false),
       show_in_launcher_(false),
       weak_ptr_factory_(this) {
+  constexpr bool allow_placeholder_icon = true;
   apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile);
 
   if (proxy) {
@@ -38,6 +39,7 @@
     proxy->LoadIcon(
         app_id, apps::mojom::IconCompression::kUncompressed,
         AppListConfig::instance().GetPreferredIconDimension(display_type()),
+        allow_placeholder_icon,
         base::BindOnce(&AppServiceAppResult::OnLoadIcon,
                        weak_ptr_factory_.GetWeakPtr(), false));
 
@@ -45,6 +47,7 @@
       proxy->LoadIcon(
           app_id, apps::mojom::IconCompression::kUncompressed,
           AppListConfig::instance().suggestion_chip_icon_dimension(),
+          allow_placeholder_icon,
           base::BindOnce(&AppServiceAppResult::OnLoadIcon,
                          weak_ptr_factory_.GetWeakPtr(), true));
     }
@@ -123,6 +126,10 @@
   } else {
     SetIcon(icon_value->uncompressed);
   }
+
+  if (icon_value->is_placeholder_icon) {
+    // TODO(crbug.com/826982): watch for new (non-placeholder) icons.
+  }
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index 7213f85..3dfc447 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -16,6 +16,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
@@ -46,6 +47,8 @@
 #include "chrome/browser/ui/page_info/page_info_dialog.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
+#include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
@@ -1302,14 +1305,30 @@
   ASSERT_TRUE(https_server()->Start());
   NavigateToURLAndWait(browser(), GetInstallableAppURL());
 
-  chrome::SetAutoAcceptPWAInstallDialogForTesting(true);
+  chrome::SetAutoAcceptPWAInstallDialogForTesting(/*auto_accept*/ true);
+
+  web_app::AppId app_id;
+
+  base::RunLoop run_loop;
+  web_app::SetInstalledCallbackForTesting(
+      base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
+                                     web_app::InstallResultCode code) {
+        EXPECT_EQ(web_app::InstallResultCode::kSuccess, code);
+        app_id = installed_app_id;
+        run_loop.Quit();
+      }));
+
   chrome::ExecuteCommand(browser(), IDC_INSTALL_PWA);
+  run_loop.Run();
+
+  chrome::SetAutoAcceptPWAInstallDialogForTesting(/*auto_accept*/ false);
+
   const extensions::Extension* app =
-      extensions::TestExtensionRegistryObserver(
-          extensions::ExtensionRegistry::Get(browser()->profile()))
-          .WaitForExtensionInstalled();
+      extensions::ExtensionRegistry::Get(browser()->profile())
+          ->enabled_extensions()
+          .GetByID(app_id);
+  ASSERT_TRUE(app);
   EXPECT_EQ(app->name(), GetInstallableAppName());
-  chrome::SetAutoAcceptPWAInstallDialogForTesting(false);
 
   // Installed PWAs should launch in their own window.
   EXPECT_EQ(extensions::GetLaunchContainer(
diff --git a/chrome/browser/ui/startup/startup_tab_provider.cc b/chrome/browser/ui/startup/startup_tab_provider.cc
index b47c854..5100310 100644
--- a/chrome/browser/ui/startup/startup_tab_provider.cc
+++ b/chrome/browser/ui/startup/startup_tab_provider.cc
@@ -76,10 +76,6 @@
   standard_params.is_signin_allowed = profile->IsSyncAllowed();
   if (auto* identity_manager = IdentityManagerFactory::GetForProfile(profile)) {
     standard_params.is_signed_in = identity_manager->HasPrimaryAccount();
-    if (auto* account_mutator = identity_manager->GetPrimaryAccountMutator()) {
-      standard_params.is_signin_in_progress =
-          account_mutator->LegacyIsPrimaryAccountAuthInProgress();
-    }
   }
   standard_params.is_supervised_user = profile->IsSupervised();
   standard_params.is_force_signin_enabled = signin_util::IsForceSigninEnabled();
@@ -204,9 +200,8 @@
 // static
 bool StartupTabProviderImpl::ShouldShowWelcomeForOnboarding(
     bool has_seen_welcome_page,
-    bool is_signed_in,
-    bool is_signin_in_progress) {
-  return !has_seen_welcome_page && !is_signed_in && !is_signin_in_progress;
+    bool is_signed_in) {
+  return !has_seen_welcome_page && !is_signed_in;
 }
 
 // static
@@ -216,8 +211,7 @@
   if (CanShowWelcome(params.is_signin_allowed, params.is_supervised_user,
                      params.is_force_signin_enabled) &&
       ShouldShowWelcomeForOnboarding(params.has_seen_welcome_page,
-                                     params.is_signed_in,
-                                     params.is_signin_in_progress)) {
+                                     params.is_signed_in)) {
     tabs.emplace_back(GetWelcomePageUrl(!params.is_first_run), false);
   }
   return tabs;
diff --git a/chrome/browser/ui/startup/startup_tab_provider.h b/chrome/browser/ui/startup/startup_tab_provider.h
index c7874ca78..e338d9a 100644
--- a/chrome/browser/ui/startup/startup_tab_provider.h
+++ b/chrome/browser/ui/startup/startup_tab_provider.h
@@ -69,7 +69,6 @@
     bool has_seen_welcome_page = false;
     bool is_signin_allowed = false;
     bool is_signed_in = false;
-    bool is_signin_in_progress = false;
     bool is_supervised_user = false;
     bool is_force_signin_enabled = false;
   };
@@ -95,8 +94,7 @@
   // Returns true if the standard welcome page should be shown in a tab. This
   // should only be used following a positive result from CanShowWelcome.
   static bool ShouldShowWelcomeForOnboarding(bool has_seen_welcome_page,
-                                             bool is_signed_in,
-                                             bool is_signin_in_progress);
+                                             bool is_signed_in);
 
   // Determines which tabs should be shown according to onboarding/first
   // run policy.
diff --git a/chrome/browser/ui/startup/startup_tab_provider_unittest.cc b/chrome/browser/ui/startup/startup_tab_provider_unittest.cc
index e6cceaf..56c0937 100644
--- a/chrome/browser/ui/startup/startup_tab_provider_unittest.cc
+++ b/chrome/browser/ui/startup/startup_tab_provider_unittest.cc
@@ -66,7 +66,7 @@
     StandardOnboardingTabsParams params;
     params.is_first_run = true;
     params.is_signin_allowed = true;
-    params.is_signin_in_progress = true;
+    params.is_signed_in = true;
     StartupTabs output =
         StartupTabProviderImpl::GetStandardOnboardingTabsForState(params);
     EXPECT_TRUE(output.empty());
@@ -262,22 +262,6 @@
     EXPECT_TRUE(output.empty());
   }
   {
-    // If sign-in is in progress, block showing the standard Welcome page.
-    StandardOnboardingTabsParams standard_params;
-    standard_params.is_first_run = true;
-    standard_params.is_signin_allowed = true;
-    standard_params.is_signin_in_progress = true;
-
-    Win10OnboardingTabsParams win10_params;
-    win10_params.has_seen_win10_promo = true;
-    win10_params.set_default_browser_allowed = true;
-
-    StartupTabs output = StartupTabProviderImpl::GetWin10OnboardingTabsForState(
-        standard_params, win10_params);
-
-    EXPECT_TRUE(output.empty());
-  }
-  {
     // If sign-in is disabled, block showing the standard Welcome page.
     StandardOnboardingTabsParams standard_params;
     standard_params.is_first_run = true;
diff --git a/chrome/browser/ui/task_manager/task_manager_table_model.cc b/chrome/browser/ui/task_manager/task_manager_table_model.cc
index 40a28b9..1025020 100644
--- a/chrome/browser/ui/task_manager/task_manager_table_model.cc
+++ b/chrome/browser/ui/task_manager/task_manager_table_model.cc
@@ -353,6 +353,13 @@
           observed_task_manager()->GetSwappedMemoryUsage(tasks_[row]), false);
 
     case IDS_TASK_MANAGER_PROCESS_ID_COLUMN:
+      if (observed_task_manager()->IsRunningInVM(tasks_[row])) {
+        // Don't show the process ID if running inside a VM to avoid confusion
+        // over conflicting pids.
+        // TODO(b/122992194): Figure out if we need to change this to display
+        // something for VM processes.
+        return base::string16();
+      }
       return stringifier_->GetProcessIdText(
           observed_task_manager()->GetProcessId(tasks_[row]));
 
@@ -502,9 +509,15 @@
           observed_task_manager()->GetNaClDebugStubPort(tasks_[row1]),
           observed_task_manager()->GetNaClDebugStubPort(tasks_[row2]));
 
-    case IDS_TASK_MANAGER_PROCESS_ID_COLUMN:
+    case IDS_TASK_MANAGER_PROCESS_ID_COLUMN: {
+      bool vm1 = observed_task_manager()->IsRunningInVM(tasks_[row1]);
+      bool vm2 = observed_task_manager()->IsRunningInVM(tasks_[row2]);
+      if (vm1 != vm2) {
+        return ValueCompare(vm1, vm2);
+      }
       return ValueCompare(observed_task_manager()->GetProcessId(tasks_[row1]),
                           observed_task_manager()->GetProcessId(tasks_[row2]));
+    }
 
     case IDS_TASK_MANAGER_GDI_HANDLES_COLUMN: {
       int64_t current1, peak1, current2, peak2;
@@ -611,17 +624,21 @@
 void TaskManagerTableModel::GetRowsGroupRange(int row_index,
                                               int* out_start,
                                               int* out_length) {
-  const base::ProcessId process_id =
-      observed_task_manager()->GetProcessId(tasks_[row_index]);
   int i = row_index;
   int limit = row_index + 1;
-  while (i > 0 &&
-         observed_task_manager()->GetProcessId(tasks_[i - 1]) == process_id) {
-    --i;
-  }
-  while (limit < RowCount() &&
-         observed_task_manager()->GetProcessId(tasks_[limit]) == process_id) {
-    ++limit;
+  if (!observed_task_manager()->IsRunningInVM(tasks_[row_index])) {
+    const base::ProcessId process_id =
+        observed_task_manager()->GetProcessId(tasks_[row_index]);
+    while (i > 0 &&
+           observed_task_manager()->GetProcessId(tasks_[i - 1]) == process_id &&
+           !observed_task_manager()->IsRunningInVM(tasks_[i - 1])) {
+      --i;
+    }
+    while (limit < RowCount() &&
+           observed_task_manager()->GetProcessId(tasks_[limit]) == process_id &&
+           !observed_task_manager()->IsRunningInVM(tasks_[limit])) {
+      ++limit;
+    }
   }
   *out_start = i;
   *out_length = limit - i;
@@ -897,8 +914,15 @@
   if (row_index == 0)
     return true;
 
-  return observed_task_manager()->GetProcessId(tasks_[row_index - 1]) !=
-      observed_task_manager()->GetProcessId(tasks_[row_index]);
+  if (observed_task_manager()->GetProcessId(tasks_[row_index - 1]) !=
+      observed_task_manager()->GetProcessId(tasks_[row_index]))
+    return true;
+
+  if (observed_task_manager()->IsRunningInVM(tasks_[row_index - 1]) !=
+      observed_task_manager()->IsRunningInVM(tasks_[row_index]))
+    return true;
+
+  return false;
 }
 
 }  // namespace task_manager
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
index 3e35be61..5f2a62d 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
@@ -162,10 +162,14 @@
   // call ExecuteCommandWithDisposition on their behalf. Unfortunately, it's not
   // possible to plumb IsKeyEvent through, so this has to be a special case.
   if (IsIncognitoCounterActive()) {
-    IncognitoWindowCountView::ShowBubble(
-        this, browser_,
-        BrowserList::GetIncognitoSessionsActiveForProfile(profile_));
+    if (!IncognitoWindowCountView::IsShowing()) {
+      IncognitoWindowCountView::ShowBubble(
+          this, browser_,
+          BrowserList::GetIncognitoSessionsActiveForProfile(profile_));
+    }
   } else {
+    // TODO(https://crbug.com/896235): Call IncognitoWindowCountView::ShowBubble
+    // from this ShowAvatarBubbleFromAvatarButton.
     browser_->window()->ShowAvatarBubbleFromAvatarButton(
         BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT,
         signin::ManageAccountsParams(),
diff --git a/chrome/browser/ui/views/profiles/incognito_window_count_view.cc b/chrome/browser/ui/views/profiles/incognito_window_count_view.cc
index 380689a..6b4bdc9 100644
--- a/chrome/browser/ui/views/profiles/incognito_window_count_view.cc
+++ b/chrome/browser/ui/views/profiles/incognito_window_count_view.cc
@@ -8,7 +8,6 @@
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/themes/theme_properties.h"
-#include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -20,12 +19,24 @@
 #include "ui/views/layout/box_layout.h"
 
 // static
+IncognitoWindowCountView*
+    IncognitoWindowCountView::incognito_window_counter_bubble_ = nullptr;
+
+// static
 void IncognitoWindowCountView::ShowBubble(views::Button* anchor_button,
                                           Browser* browser,
                                           int incognito_window_count) {
   // The IncognitoWindowCountView is self-owned, it deletes itself when the
   // widget is closed or the parent browser is destroyed.
-  new IncognitoWindowCountView(anchor_button, browser, incognito_window_count);
+  if (!IsShowing()) {
+    new IncognitoWindowCountView(anchor_button, browser,
+                                 incognito_window_count);
+  }
+}
+
+// static
+bool IncognitoWindowCountView::IsShowing() {
+  return incognito_window_counter_bubble_ != nullptr;
 }
 
 IncognitoWindowCountView::IncognitoWindowCountView(views::Button* anchor_button,
@@ -36,6 +47,8 @@
       browser_(browser),
       browser_list_observer_(this),
       weak_ptr_factory_(this) {
+  DCHECK(incognito_window_counter_bubble_ == nullptr);
+  incognito_window_counter_bubble_ = this;
   browser_list_observer_.Add(BrowserList::GetInstance());
 
   // The lifetime of this bubble is tied to the lifetime of the browser.
@@ -48,7 +61,9 @@
       chrome::DialogIdentifier::INCOGNITO_WINDOW_COUNTER);
 }
 
-IncognitoWindowCountView::~IncognitoWindowCountView() {}
+IncognitoWindowCountView::~IncognitoWindowCountView() {
+  incognito_window_counter_bubble_ = nullptr;
+}
 
 void IncognitoWindowCountView::OnBrowserRemoved(Browser* browser) {
   if (browser_ == browser)
@@ -64,10 +79,8 @@
       views::BoxLayout::Orientation::kVertical));
 
   auto incognito_icon = std::make_unique<views::ImageView>();
-  const ui::ThemeProvider& theme_provider =
-      ThemeService::GetThemeProviderForProfile(browser_->profile());
-  const SkColor icon_color =
-      theme_provider.GetColor(ThemeProperties::COLOR_NTP_BACKGROUND);
+  const SkColor icon_color = ThemeProperties::GetDefaultColor(
+      ThemeProperties::COLOR_NTP_BACKGROUND, true /* incognito */);
   incognito_icon->SetImage(
       gfx::CreateVectorIcon(kIncognitoIcon, 40, icon_color));
 
diff --git a/chrome/browser/ui/views/profiles/incognito_window_count_view.h b/chrome/browser/ui/views/profiles/incognito_window_count_view.h
index 4ef8747e..abfa622 100644
--- a/chrome/browser/ui/views/profiles/incognito_window_count_view.h
+++ b/chrome/browser/ui/views/profiles/incognito_window_count_view.h
@@ -25,6 +25,8 @@
                          Browser* browser,
                          int incognito_window_count);
 
+  static bool IsShowing();
+
   // BubbleDialogDelegateView:
   int GetDialogButtons() const override;
   void Init() override;
@@ -46,6 +48,8 @@
   int incognito_window_count_;
   Browser* const browser_;
 
+  static IncognitoWindowCountView* incognito_window_counter_bubble_;
+
   ScopedObserver<BrowserList, BrowserListObserver> browser_list_observer_;
   base::WeakPtrFactory<IncognitoWindowCountView> weak_ptr_factory_{this};
 
diff --git a/chrome/browser/ui/views/select_file_dialog_extension.cc b/chrome/browser/ui/views/select_file_dialog_extension.cc
index 7acbb09..96831291 100644
--- a/chrome/browser/ui/views/select_file_dialog_extension.cc
+++ b/chrome/browser/ui/views/select_file_dialog_extension.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/chromeos/file_manager/select_file_dialog_util.h"
 #include "chrome/browser/chromeos/file_manager/url_util.h"
 #include "chrome/browser/chromeos/login/ui/login_web_dialog.h"
+#include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_view_host.h"
 #include "chrome/browser/profiles/profile.h"
@@ -31,7 +32,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/views/extensions/extension_dialog.h"
 #include "chrome/common/pref_names.h"
-#include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/native_app_window.h"
@@ -369,11 +369,8 @@
   if (PendingExists(routing_id))
     return;
 
-  const PrefService* pref_service = profile_->GetPrefs();
-  DCHECK(pref_service);
-
   base::FilePath download_default_path(
-      pref_service->GetFilePath(prefs::kDownloadDefaultDirectory));
+      DownloadPrefs::FromBrowserContext(profile_)->DownloadPath());
 
   base::FilePath selection_path = default_path.IsAbsolute() ?
       default_path : download_default_path.Append(default_path.BaseName());
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
new file mode 100644
index 0000000..d5b80457
--- /dev/null
+++ b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.cc
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.h"
+
+#include <memory>
+#include <utility>
+
+AuthenticatorClientPinEntrySheetView::AuthenticatorClientPinEntrySheetView(
+    std::unique_ptr<AuthenticatorClientPinEntrySheetModel> sheet_model)
+    : AuthenticatorRequestSheetView(std::move(sheet_model)) {
+  pin_entry_sheet_model()->SetDelegate(this);
+}
+
+AuthenticatorClientPinEntrySheetView::~AuthenticatorClientPinEntrySheetView() =
+    default;
+
+AuthenticatorClientPinEntrySheetModel*
+AuthenticatorClientPinEntrySheetView::pin_entry_sheet_model() {
+  return static_cast<AuthenticatorClientPinEntrySheetModel*>(model());
+}
+
+std::unique_ptr<views::View>
+AuthenticatorClientPinEntrySheetView::BuildStepSpecificContent() {
+  DCHECK(!pin_entry_view_);
+  auto view = std::make_unique<AuthenticatorClientPinEntryView>(
+      this, pin_entry_sheet_model()->mode() ==
+                AuthenticatorClientPinEntrySheetModel::Mode::
+                    kPinSetup /* show_confirmation_text_field */);
+  pin_entry_view_ = view.get();
+  return view;
+}
+
+void AuthenticatorClientPinEntrySheetView::OnPincodeChanged(
+    base::string16 pincode) {
+  pin_entry_sheet_model()->SetPinCode(std::move(pincode));
+}
+
+void AuthenticatorClientPinEntrySheetView::OnConfirmationChanged(
+    base::string16 pincode) {
+  pin_entry_sheet_model()->SetPinConfirmation(std::move(pincode));
+}
+
+void AuthenticatorClientPinEntrySheetView::ShowPinError(
+    const base::string16& error) {
+  if (!pin_entry_view_) {
+    DCHECK(false);
+    return;
+  }
+  pin_entry_view_->UpdateError(error);
+}
diff --git a/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.h b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.h
new file mode 100644
index 0000000..dadca87
--- /dev/null
+++ b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.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 CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_CLIENT_PIN_ENTRY_SHEET_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_CLIENT_PIN_ENTRY_SHEET_VIEW_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_view.h"
+#include "chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h"
+#include "chrome/browser/ui/webauthn/sheet_models.h"
+
+// Web Authentication request dialog sheet view for entering an authenticator
+// PIN.
+class AuthenticatorClientPinEntrySheetView
+    : public AuthenticatorRequestSheetView,
+      public AuthenticatorClientPinEntrySheetModel::Delegate,
+      public AuthenticatorClientPinEntryView::Delegate {
+ public:
+  explicit AuthenticatorClientPinEntrySheetView(
+      std::unique_ptr<AuthenticatorClientPinEntrySheetModel> model);
+  ~AuthenticatorClientPinEntrySheetView() override;
+
+ private:
+  AuthenticatorClientPinEntrySheetModel* pin_entry_sheet_model();
+
+  // AuthenticatorRequestSheetView:
+  std::unique_ptr<views::View> BuildStepSpecificContent() override;
+
+  // AuthenticatorClientPinEntrySheetModel::Delegate:
+  void ShowPinError(const base::string16& error) override;
+
+  // AuthenticatorClientPinEntryView::Delegate:
+  void OnPincodeChanged(base::string16 pincode) override;
+  void OnConfirmationChanged(base::string16 pincode) override;
+
+  AuthenticatorClientPinEntryView* pin_entry_view_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(AuthenticatorClientPinEntrySheetView);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_CLIENT_PIN_ENTRY_SHEET_VIEW_H_
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
new file mode 100644
index 0000000..61c301c6
--- /dev/null
+++ b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_view.cc
@@ -0,0 +1,140 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_view.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/layout_provider.h"
+#include "ui/views/style/typography.h"
+
+namespace {
+
+constexpr int kExpectedPincodeCharLength = 6;
+constexpr int kPreferredTextfieldCharLength = 20;
+constexpr int kTextfieldBottomBorderThickness = 2;
+
+}  // namespace
+
+AuthenticatorClientPinEntryView::AuthenticatorClientPinEntryView(
+    Delegate* delegate,
+    bool show_confirmation_text_field)
+    : delegate_(delegate),
+      show_confirmation_text_field_(show_confirmation_text_field) {
+  views::GridLayout* layout =
+      SetLayoutManager(std::make_unique<views::GridLayout>(this));
+  views::ColumnSet* columns = layout->AddColumnSet(0);
+
+  columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING,
+                     views::GridLayout::kFixedSize, views::GridLayout::USE_PREF,
+                     0, 0);
+  columns->AddPaddingColumn(views::GridLayout::kFixedSize, 10);
+  columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING,
+                     views::GridLayout::kFixedSize, views::GridLayout::USE_PREF,
+                     0, 0);
+
+  layout->StartRow(views::GridLayout::kFixedSize, 0);
+
+  auto pin_label = std::make_unique<views::Label>(
+      l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_PIN_LABEL),
+      views::style::CONTEXT_LABEL, views::style::STYLE_PRIMARY);
+  pin_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  pin_label->SetEnabledColor(gfx::kGoogleBlue500);
+  auto* pin_label_ptr = pin_label.get();
+  layout->AddView(pin_label.release());
+
+  views::View* confirmation_label_ptr = nullptr;
+  if (show_confirmation_text_field_) {
+    auto confirmation_label = std::make_unique<views::Label>(
+        l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_SETUP_CONFIRMATION_LABEL),
+        views::style::CONTEXT_LABEL, views::style::STYLE_PRIMARY);
+    confirmation_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    confirmation_label->SetEnabledColor(gfx::kGoogleBlue500);
+    confirmation_label_ptr = confirmation_label.get();
+    layout->AddView(confirmation_label.release());
+  }
+
+  layout->StartRow(views::GridLayout::kFixedSize, 0);
+
+  auto pin_text_field = std::make_unique<views::Textfield>();
+  pin_text_field->SetTextInputType(ui::TextInputType::TEXT_INPUT_TYPE_PASSWORD);
+  pin_text_field->SetBackgroundColor(gfx::kGoogleGrey100);
+  pin_text_field->SetMinimumWidthInChars(kExpectedPincodeCharLength);
+  pin_text_field->SetDefaultWidthInChars(kPreferredTextfieldCharLength);
+  pin_text_field->SetBorder(views::CreateSolidSidedBorder(
+      0, 0, kTextfieldBottomBorderThickness, 0, gfx::kGoogleBlue500));
+  pin_text_field->set_controller(this);
+  pin_text_field->SetAssociatedLabel(pin_label_ptr);
+  pin_text_field_ = pin_text_field.get();
+  layout->AddView(pin_text_field.release());
+
+  if (show_confirmation_text_field_) {
+    DCHECK(confirmation_label_ptr);
+    auto confirmation_text_field = std::make_unique<views::Textfield>();
+    confirmation_text_field->SetTextInputType(
+        ui::TextInputType::TEXT_INPUT_TYPE_PASSWORD);
+    confirmation_text_field->SetBackgroundColor(gfx::kGoogleGrey100);
+    confirmation_text_field->SetMinimumWidthInChars(kExpectedPincodeCharLength);
+    confirmation_text_field->SetDefaultWidthInChars(
+        kPreferredTextfieldCharLength);
+    confirmation_text_field->SetBorder(views::CreateSolidSidedBorder(
+        0, 0, kTextfieldBottomBorderThickness, 0, gfx::kGoogleBlue500));
+    confirmation_text_field->set_controller(this);
+    confirmation_text_field->SetAssociatedLabel(confirmation_label_ptr);
+    confirmation_text_field_ = confirmation_text_field.get();
+    layout->AddView(confirmation_text_field.release());
+  }
+
+  layout->StartRow(views::GridLayout::kFixedSize, 0);
+
+  auto error_label = std::make_unique<views::Label>(
+      base::string16(), views::style::CONTEXT_LABEL,
+      views::style::STYLE_PRIMARY);
+  error_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  error_label->SetEnabledColor(gfx::kGoogleRed500);
+  error_label_ = error_label.get();
+  layout->AddView(error_label.release(), 3 /* col_span */, 1 /* row_span */);
+}
+
+AuthenticatorClientPinEntryView::~AuthenticatorClientPinEntryView() = default;
+
+void AuthenticatorClientPinEntryView::UpdateError(const base::string16& text) {
+  error_label_->SetVisible(true);
+  error_label_->SetText(text);
+  error_label_->SizeToPreferredSize();
+}
+
+void AuthenticatorClientPinEntryView::RequestFocus() {
+  pin_text_field_->RequestFocus();
+}
+
+void AuthenticatorClientPinEntryView::ContentsChanged(
+    views::Textfield* sender,
+    const base::string16& new_contents) {
+  DCHECK(sender == pin_text_field_ || sender == confirmation_text_field_);
+
+  if (sender == pin_text_field_) {
+    delegate_->OnPincodeChanged(new_contents);
+  } else {
+    delegate_->OnConfirmationChanged(new_contents);
+  }
+}
+
+bool AuthenticatorClientPinEntryView::HandleKeyEvent(
+    views::Textfield* sender,
+    const ui::KeyEvent& key_event) {
+  // As WebAuthN UI views do not intercept any key events, the key event must
+  // be further processed.
+  return false;
+}
diff --git a/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_view.h b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_view.h
new file mode 100644
index 0000000..47eee05aa
--- /dev/null
+++ b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_view.h
@@ -0,0 +1,57 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_CLIENT_PIN_ENTRY_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_CLIENT_PIN_ENTRY_VIEW_H_
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
+#include "ui/views/controls/textfield/textfield_controller.h"
+#include "ui/views/view.h"
+
+namespace views {
+class Textfield;
+class Label;
+}  // namespace views
+
+// View showing a label and text field for entering an authenticator PIN.
+//
+// TODO(martinkr): Reuse for BLE PIN or fold into
+// AuthenticatorClientPinEntrySheetModel.
+class AuthenticatorClientPinEntryView : public views::View,
+                                        public views::TextfieldController {
+ public:
+  class Delegate {
+   public:
+    virtual void OnPincodeChanged(base::string16 pin_code) = 0;
+    virtual void OnConfirmationChanged(base::string16 pin_confirmation) = 0;
+  };
+
+  explicit AuthenticatorClientPinEntryView(Delegate* delegate,
+                                           bool show_confirmation_text_field);
+  ~AuthenticatorClientPinEntryView() override;
+
+  void UpdateError(const base::string16& value);
+
+ private:
+  // views::View:
+  void RequestFocus() override;
+
+  // views::TextFieldController:
+  void ContentsChanged(views::Textfield* sender,
+                       const base::string16& new_contents) override;
+  bool HandleKeyEvent(views::Textfield* sender,
+                      const ui::KeyEvent& key_event) override;
+
+  Delegate* const delegate_;
+  views::Textfield* pin_text_field_ = nullptr;
+  views::Textfield* confirmation_text_field_ = nullptr;
+  views::Label* error_label_ = nullptr;
+  const bool show_confirmation_text_field_;
+
+  DISALLOW_COPY_AND_ASSIGN(AuthenticatorClientPinEntryView);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_CLIENT_PIN_ENTRY_VIEW_H_
diff --git a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
index 03053d1..bdff790 100644
--- a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
+++ b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/ui/views/webauthn/authenticator_ble_pin_entry_sheet_view.h"
+#include "chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.h"
 #include "chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h"
 #include "chrome/browser/ui/views/webauthn/authenticator_transport_selector_sheet_view.h"
 #include "chrome/browser/ui/views/webauthn/ble_device_selection_sheet_view.h"
@@ -118,6 +119,18 @@
       sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
           std::make_unique<AuthenticatorPaaskSheetModel>(dialog_model));
       break;
+    case Step::kClientPinEntry:
+      sheet_view = std::make_unique<AuthenticatorClientPinEntrySheetView>(
+          std::make_unique<AuthenticatorClientPinEntrySheetModel>(
+              dialog_model,
+              AuthenticatorClientPinEntrySheetModel::Mode::kPinEntry));
+      break;
+    case Step::kClientPinSetup:
+      sheet_view = std::make_unique<AuthenticatorClientPinEntrySheetView>(
+          std::make_unique<AuthenticatorClientPinEntrySheetModel>(
+              dialog_model,
+              AuthenticatorClientPinEntrySheetModel::Mode::kPinSetup));
+      break;
     case Step::kNotStarted:
     case Step::kClosed:
       sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
diff --git a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
index a68338004..1d979a2 100644
--- a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
+++ b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/feature_list.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/web_applications/components/install_manager.h"
@@ -54,6 +55,16 @@
   }
 }
 
+WebAppInstalledCallbackForTesting& GetInstalledCallbackForTesting() {
+  static base::NoDestructor<WebAppInstalledCallbackForTesting> instance;
+  return *instance;
+}
+
+void OnWebAppInstalled(const AppId& installed_app_id, InstallResultCode code) {
+  if (GetInstalledCallbackForTesting())
+    std::move(GetInstalledCallbackForTesting()).Run(installed_app_id, code);
+}
+
 }  // namespace
 
 bool CanCreateWebApp(const Browser* browser) {
@@ -77,7 +88,13 @@
 
   provider->install_manager().InstallWebApp(
       web_contents, force_shortcut_app,
-      base::BindOnce(WebAppInstallDialogCallback), base::DoNothing());
+      base::BindOnce(WebAppInstallDialogCallback),
+      base::BindOnce(OnWebAppInstalled));
+}
+
+void SetInstalledCallbackForTesting(
+    WebAppInstalledCallbackForTesting callback) {
+  GetInstalledCallbackForTesting() = std::move(callback);
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/ui/web_applications/web_app_dialog_utils.h b/chrome/browser/ui/web_applications/web_app_dialog_utils.h
index e47c16f..f77d132e 100644
--- a/chrome/browser/ui/web_applications/web_app_dialog_utils.h
+++ b/chrome/browser/ui/web_applications/web_app_dialog_utils.h
@@ -5,10 +5,15 @@
 #ifndef CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_DIALOG_UTILS_H_
 #define CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_DIALOG_UTILS_H_
 
+#include "base/callback_forward.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+
 class Browser;
 
 namespace web_app {
 
+enum class InstallResultCode;
+
 // Returns true if a WebApp installation is allowed for the current page.
 bool CanCreateWebApp(const Browser* browser);
 
@@ -16,6 +21,10 @@
 void CreateWebAppFromCurrentWebContents(Browser* browser,
                                         bool force_shortcut_app);
 
+using WebAppInstalledCallbackForTesting =
+    base::OnceCallback<void(const AppId& app_id, InstallResultCode code)>;
+void SetInstalledCallbackForTesting(WebAppInstalledCallbackForTesting callback);
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_DIALOG_UTILS_H_
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc
index bb9f298..fca6f7a 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/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/webauthn/other_transports_menu_model.h"
@@ -662,3 +663,100 @@
 ui::MenuModel* AuthenticatorPaaskSheetModel::GetOtherTransportsMenuModel() {
   return other_transports_menu_model_.get();
 }
+
+// AuthenticatorClientPinEntrySheetModel
+// -----------------------------------------
+
+AuthenticatorClientPinEntrySheetModel::AuthenticatorClientPinEntrySheetModel(
+    AuthenticatorRequestDialogModel* dialog_model,
+    Mode mode)
+    : AuthenticatorSheetModelBase(dialog_model), mode_(mode) {}
+
+AuthenticatorClientPinEntrySheetModel::
+    ~AuthenticatorClientPinEntrySheetModel() = default;
+
+void AuthenticatorClientPinEntrySheetModel::SetDelegate(Delegate* delegate) {
+  DCHECK(!delegate_);
+  delegate_ = delegate;
+}
+
+void AuthenticatorClientPinEntrySheetModel::SetPinCode(
+    base::string16 pin_code) {
+  pin_code_ = std::move(pin_code);
+}
+
+void AuthenticatorClientPinEntrySheetModel::SetPinConfirmation(
+    base::string16 pin_confirmation) {
+  DCHECK(mode_ == AuthenticatorClientPinEntrySheetModel::Mode::kPinSetup);
+  pin_confirmation_ = std::move(pin_confirmation);
+}
+
+gfx::ImageSkia* AuthenticatorClientPinEntrySheetModel::GetStepIllustration()
+    const {
+  return GetImage(IDR_WEBAUTHN_ILLUSTRATION_PIN);
+}
+
+base::string16 AuthenticatorClientPinEntrySheetModel::GetStepTitle() const {
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_TITLE);
+}
+
+base::string16 AuthenticatorClientPinEntrySheetModel::GetStepDescription()
+    const {
+  return l10n_util::GetStringUTF16(
+      mode_ == AuthenticatorClientPinEntrySheetModel::Mode::kPinEntry
+          ? IDS_WEBAUTHN_PIN_ENTRY_DESCRIPTION
+          : IDS_WEBAUTHN_PIN_SETUP_DESCRIPTION);
+}
+
+bool AuthenticatorClientPinEntrySheetModel::IsAcceptButtonVisible() const {
+  return true;
+}
+
+bool AuthenticatorClientPinEntrySheetModel::IsAcceptButtonEnabled() const {
+  return true;
+}
+
+base::string16 AuthenticatorClientPinEntrySheetModel::GetAcceptButtonLabel()
+    const {
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_NEXT);
+}
+
+static bool IsValidUTF16(const base::string16& str16) {
+  std::string unused_str8;
+  return base::UTF16ToUTF8(str16.c_str(), str16.size(), &unused_str8);
+}
+
+void AuthenticatorClientPinEntrySheetModel::OnAccept() {
+  // TODO(martinkr): use device::pin::kMinLength once landed.
+  constexpr size_t kMinPinLength = 4;
+  if (!delegate_) {
+    NOTREACHED();
+    return;
+  }
+  if (mode_ == AuthenticatorClientPinEntrySheetModel::Mode::kPinSetup) {
+    // Validate a new PIN.
+    base::Optional<base::string16> error;
+    if (!pin_code_.empty() && !IsValidUTF16(pin_code_)) {
+      error = l10n_util::GetStringUTF16(
+          IDS_WEBAUTHN_PIN_ENTRY_ERROR_INVALID_CHARACTERS);
+    } else if (pin_code_.size() < kMinPinLength) {
+      error = l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_ERROR_TOO_SHORT);
+    } else if (pin_code_ != pin_confirmation_) {
+      error = l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_ERROR_MISMATCH);
+    }
+    if (error) {
+      delegate_->ShowPinError(*error);
+      return;
+    }
+  } else {
+    // Submit PIN to authenticator for verification.
+    DCHECK(mode_ == AuthenticatorClientPinEntrySheetModel::Mode::kPinEntry);
+    if (pin_code_.size() < kMinPinLength) {
+      delegate_->ShowPinError(
+          l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_ERROR_TOO_SHORT));
+      return;
+    }
+  }
+  // TODO(martinkr): Actually set the PIN/request the PIN token and continue
+  // the request.
+}
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h
index 4ba545c..8ababa8 100644
--- a/chrome/browser/ui/webauthn/sheet_models.h
+++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -344,4 +344,43 @@
   std::unique_ptr<OtherTransportsMenuModel> other_transports_menu_model_;
 };
 
+class AuthenticatorClientPinEntrySheetModel
+    : public AuthenticatorSheetModelBase {
+ public:
+  class Delegate {
+   public:
+    virtual void ShowPinError(const base::string16& error) = 0;
+  };
+  // Indicates whether the view should accommodate setting up a new PIN or
+  // entering an existing one.
+  enum class Mode { kPinEntry, kPinSetup };
+  AuthenticatorClientPinEntrySheetModel(
+      AuthenticatorRequestDialogModel* dialog_model,
+      Mode mode);
+  ~AuthenticatorClientPinEntrySheetModel() override;
+
+  using AuthenticatorSheetModelBase::AuthenticatorSheetModelBase;
+
+  void SetDelegate(Delegate* delegate);
+  void SetPinCode(base::string16 pin_code);
+  void SetPinConfirmation(base::string16 pin_confirmation);
+
+  Mode mode() const { return mode_; }
+
+ private:
+  // AuthenticatorSheetModelBase:
+  gfx::ImageSkia* GetStepIllustration() const override;
+  base::string16 GetStepTitle() const override;
+  base::string16 GetStepDescription() const override;
+  bool IsAcceptButtonVisible() const override;
+  bool IsAcceptButtonEnabled() const override;
+  base::string16 GetAcceptButtonLabel() const override;
+  void OnAccept() override;
+
+  base::string16 pin_code_;
+  base::string16 pin_confirmation_;
+  const Mode mode_;
+  Delegate* delegate_ = nullptr;
+};
+
 #endif  // CHROME_BROWSER_UI_WEBAUTHN_SHEET_MODELS_H_
diff --git a/chrome/browser/ui/webui/usb_internals/usb_internals_ui.cc b/chrome/browser/ui/webui/usb_internals/usb_internals_ui.cc
index 0ee734c..33867b01 100644
--- a/chrome/browser/ui/webui/usb_internals/usb_internals_ui.cc
+++ b/chrome/browser/ui/webui/usb_internals/usb_internals_ui.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ui/webui/usb_internals/usb_internals_ui.h"
 
-#include <utility>
-
 #include "base/bind.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.h"
diff --git a/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.cc b/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.cc
index a202a5db..8974b665 100644
--- a/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.cc
+++ b/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.cc
@@ -20,10 +20,10 @@
 
 enum class NtpBackgrounds {
   kArt = 0,
-  kLandscape = 1,
-  kCityscape = 2,
-  kSeascape = 3,
-  kGeometricShapes = 4,
+  kCityscape = 1,
+  kGeometricShapes = 2,
+  kLandscape = 3,
+  kLife = 4,
 };
 
 NtpBackgroundHandler::NtpBackgroundHandler() {}
@@ -47,12 +47,13 @@
       GetOnboardingNtpBackgrounds();
 
   auto element = std::make_unique<base::DictionaryValue>();
-  int id = static_cast<int>(NtpBackgrounds::kArt);
+  int id = static_cast<int>(NtpBackgrounds::kCityscape);
   element->SetInteger("id", id);
-  element->SetString("title",
-                     l10n_util::GetStringUTF8(
-                         IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_ART_TITLE));
+  element->SetString(
+      "title", l10n_util::GetStringUTF8(
+                   IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_CITYSCAPE_TITLE));
   element->SetString("imageUrl", onboardingNtpBackgrounds[id].spec());
+  element->SetString("thumbnailClass", "cityscape");
   list_value.Append(std::move(element));
 
   element = std::make_unique<base::DictionaryValue>();
@@ -62,24 +63,17 @@
       "title", l10n_util::GetStringUTF8(
                    IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_LANDSCAPE_TITLE));
   element->SetString("imageUrl", onboardingNtpBackgrounds[id].spec());
+  element->SetString("thumbnailClass", "landscape");
   list_value.Append(std::move(element));
 
   element = std::make_unique<base::DictionaryValue>();
-  id = static_cast<int>(NtpBackgrounds::kCityscape);
-  element->SetInteger("id", id);
-  element->SetString(
-      "title", l10n_util::GetStringUTF8(
-                   IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_CITYSCAPE_TITLE));
-  element->SetString("imageUrl", onboardingNtpBackgrounds[id].spec());
-  list_value.Append(std::move(element));
-
-  element = std::make_unique<base::DictionaryValue>();
-  id = static_cast<int>(NtpBackgrounds::kSeascape);
+  id = static_cast<int>(NtpBackgrounds::kArt);
   element->SetInteger("id", id);
   element->SetString("title",
                      l10n_util::GetStringUTF8(
-                         IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_SEASCAPE_TITLE));
+                         IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_ART_TITLE));
   element->SetString("imageUrl", onboardingNtpBackgrounds[id].spec());
+  element->SetString("thumbnailClass", "art");
   list_value.Append(std::move(element));
 
   element = std::make_unique<base::DictionaryValue>();
@@ -90,6 +84,17 @@
       l10n_util::GetStringUTF8(
           IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_GEOMETRIC_SHAPES_TITLE));
   element->SetString("imageUrl", onboardingNtpBackgrounds[id].spec());
+  element->SetString("thumbnailClass", "geometric-shapes");
+  list_value.Append(std::move(element));
+
+  element = std::make_unique<base::DictionaryValue>();
+  id = static_cast<int>(NtpBackgrounds::kLife);
+  element->SetInteger("id", id);
+  element->SetString("title",
+                     l10n_util::GetStringUTF8(
+                         IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_LIFE_TITLE));
+  element->SetString("imageUrl", onboardingNtpBackgrounds[id].spec());
+  element->SetString("thumbnailClass", "life");
   list_value.Append(std::move(element));
 
   ResolveJavascriptCallback(*callback_id, list_value);
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
index be5869f3..9519633 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
@@ -9,9 +9,11 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/extensions/bookmark_app_util.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/web_application_info.h"
 #include "extensions/common/extension.h"
@@ -28,6 +30,9 @@
 void BookmarkAppInstallFinalizer::FinalizeInstall(
     std::unique_ptr<WebApplicationInfo> web_app_info,
     InstallFinalizedCallback callback) {
+  // Concurrent calls are not allowed.
+  DCHECK(!web_app_info_);
+
   if (!crx_installer_) {
     ExtensionService* extension_service =
         ExtensionSystem::Get(profile_)->extension_service();
@@ -35,10 +40,13 @@
     crx_installer_ = CrxInstaller::CreateSilent(extension_service);
   }
 
-  crx_installer_->set_installer_callback(base::BindOnce(
-      &BookmarkAppInstallFinalizer::OnInstall, weak_ptr_factory_.GetWeakPtr(),
-      std::move(callback), web_app_info->app_url));
-  crx_installer_->InstallWebApp(*web_app_info);
+  web_app_info_ = std::move(web_app_info);
+
+  crx_installer_->set_installer_callback(
+      base::BindOnce(&BookmarkAppInstallFinalizer::OnExtensionInstalled,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                     web_app_info_->app_url));
+  crx_installer_->InstallWebApp(*web_app_info_);
 }
 
 void BookmarkAppInstallFinalizer::SetCrxInstallerForTesting(
@@ -46,22 +54,36 @@
   crx_installer_ = crx_installer;
 }
 
-void BookmarkAppInstallFinalizer::OnInstall(
+void BookmarkAppInstallFinalizer::OnExtensionInstalled(
     InstallFinalizedCallback callback,
     const GURL& app_url,
     const base::Optional<CrxInstallError>& error) {
+  DCHECK(web_app_info_);
+
   if (error) {
     std::move(callback).Run(web_app::AppId(),
                             web_app::InstallResultCode::kFailedUnknownReason);
-    return;
+  } else {
+    auto* extension = crx_installer_->extension();
+    DCHECK(extension);
+    DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(extension), app_url);
+
+    LaunchType launch_type = web_app_info_->open_as_window
+                                 ? LAUNCH_TYPE_WINDOW
+                                 : LAUNCH_TYPE_REGULAR;
+
+    // Set the launcher type for the app.
+    SetLaunchType(profile_, extension->id(), launch_type);
+
+    // Set this app to be locally installed, as it was installed from this
+    // machine.
+    SetBookmarkAppIsLocallyInstalled(profile_, extension, true);
+
+    std::move(callback).Run(extension->id(),
+                            web_app::InstallResultCode::kSuccess);
   }
 
-  auto* installed_extension = crx_installer_->extension();
-  DCHECK(installed_extension);
-  DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(installed_extension), app_url);
-
-  std::move(callback).Run(installed_extension->id(),
-                          web_app::InstallResultCode::kSuccess);
+  web_app_info_ = nullptr;
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
index 6c5db547..9b1afb1 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
@@ -36,10 +36,11 @@
   void SetCrxInstallerForTesting(scoped_refptr<CrxInstaller> crx_installer);
 
  private:
-  void OnInstall(InstallFinalizedCallback callback,
-                 const GURL& app_url,
-                 const base::Optional<CrxInstallError>& error);
+  void OnExtensionInstalled(InstallFinalizedCallback callback,
+                            const GURL& app_url,
+                            const base::Optional<CrxInstallError>& error);
 
+  std::unique_ptr<WebApplicationInfo> web_app_info_;
   scoped_refptr<CrxInstaller> crx_installer_;
   Profile* profile_;
 
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h
index 160c93e..fbd7957 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.h
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -79,6 +79,10 @@
 
     // Phone as a security key.
     kCableActivate,
+
+    // Authenticator Client PIN.
+    kClientPinEntry,
+    kClientPinSetup,
   };
 
   // Implemented by the dialog to observe this model and show the UI panels
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 67547b5..f7ff67f 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -682,6 +682,10 @@
 const base::Feature kShillSandboxing{"ShillSandboxing",
                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enable support for multiple scheduler configurations.
+const base::Feature kSchedulerConfiguration{"SchedulerConfiguration",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
 #endif  // defined(OS_CHROMEOS)
 
 // Enable showing a tab-modal dialog while a Web Authentication API request is
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index b97c853c..323e8fb 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -453,6 +453,9 @@
 COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kUsbbouncer;
 
 COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kShillSandboxing;
+
+COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kSchedulerConfiguration;
 #endif  // defined(OS_CHROMEOS)
 
 COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/common/extensions/api/automation.idl b/chrome/common/extensions/api/automation.idl
index 4ac4c913..e965dab 100644
--- a/chrome/common/extensions/api/automation.idl
+++ b/chrome/common/extensions/api/automation.idl
@@ -302,9 +302,9 @@
     scrollForward,
     scrollLeft,
     scrollRight,
+    scrollUp,
     scrollToMakeVisible,
     scrollToPoint,
-    scrollUp,
     setAccessibilityFocus,
     setScrollOffset,
     setSelection,
diff --git a/chrome/services/app_service/README.md b/chrome/services/app_service/README.md
index e29a7e3..75a593b 100644
--- a/chrome/services/app_service/README.md
+++ b/chrome/services/app_service/README.md
@@ -280,10 +280,10 @@
       // App Icon Factory methods.
       LoadIcon(
           AppType app_type,
-          string app_id,
           IconKey icon_key,
           IconCompression icon_compression,
-          int32 size_hint_in_dip) => (IconValue icon_value);
+          int32 size_hint_in_dip,
+          bool allow_placeholder_icon) => (IconValue icon_value);
 
       // Some additional methods; not App Icon Factory related.
     };
@@ -291,16 +291,18 @@
     interface Publisher {
       // App Icon Factory methods.
       LoadIcon(
-          string app_id,
           IconKey icon_key,
           IconCompression icon_compression,
-          int32 size_hint_in_dip) => (IconValue icon_value);
+          int32 size_hint_in_dip,
+          bool allow_placeholder_icon) => (IconValue icon_value);
 
       // Some additional methods; not App Icon Factory related.
     };
 
     enum IconType {
       kUnknown,
+      kArc,
+      kCrostini,
       kExtension,
       kResource,
     };
@@ -322,12 +324,44 @@
       IconCompression icon_compression;
       gfx.mojom.ImageSkia? uncompressed;
       array<uint8>? compressed;
+      bool is_placeholder_icon;
     };
 
 TBD: post-processing effects like rounded corners, badges or grayed out (for
 disabled apps) icons.
 
 
+## Placeholder Icons
+
+It can take some time for `Publisher`s to provide an icon. For example, loading
+the canonical icon for an ARC++ or Crostini app might require waiting for a VM
+to start. Such icons are often cached on the file system, but on a cache miss,
+there may be a number of seconds before the system can present an icon. In this
+case, we might want to present a `Publisher`-specific placeholder, typically
+loaded from a resource (an asset statically compiled into the binary).
+
+There are two boolean fields that facilitate this: `allow_placeholder_icon` is
+sent from a `Subsciber` to a `Publisher` and `is_placeholder_icon` is sent in
+the response.
+
+`LoadIcon`'s `allow_placeholder_icon` states whether the the caller will accept
+a placeholder if the real icon can not be provided quickly. Native user
+interfaces like the app launcher will probably set this to true. On the other
+hand, serving Web-UI URLs such as `chrome://app-icon/app_id/icon_size` will set
+this to false, as that URL should identify a particular icon, not one that
+changes over time. Web-UI that wants to display placeholder icons and be
+notified of when real icons are ready will require some mechanism other than a
+`chrome:://app-icon/etc` URL.
+
+`IconValue`'s `is_placeholder_icon` states whether the icon provided is a
+placeholder. That field should only be true if the corresponding `LoadIcon`
+call had `allow_placeholder_icon` true. When the `LoadIcon` caller receives a
+placeholder icon, it is up to the caller to issue a new `LoadIcon` call, this
+time with `allow_placeholder_icon` false. A new Mojo call is necessary, because
+a Mojo callback is a `base::OnceCallback`, so the same callback can't be used
+for both the placeholder and the real icon.
+
+
 # App Runner
 
 Each `Publisher` has (`Publisher`-specific) implementations of e.g. launching an
@@ -383,4 +417,4 @@
 
 ---
 
-Updated on 2018-11-22.
+Updated on 2019-02-09.
diff --git a/chrome/services/app_service/app_service_impl.cc b/chrome/services/app_service/app_service_impl.cc
index 5c62ace7..33c083f 100644
--- a/chrome/services/app_service/app_service_impl.cc
+++ b/chrome/services/app_service/app_service_impl.cc
@@ -64,18 +64,19 @@
 }
 
 void AppServiceImpl::LoadIcon(apps::mojom::AppType app_type,
-                              const std::string& app_id,
                               apps::mojom::IconKeyPtr icon_key,
                               apps::mojom::IconCompression icon_compression,
                               int32_t size_hint_in_dip,
+                              bool allow_placeholder_icon,
                               LoadIconCallback callback) {
   auto iter = publishers_.find(app_type);
   if (iter == publishers_.end()) {
     std::move(callback).Run(apps::mojom::IconValue::New());
     return;
   }
-  iter->second->LoadIcon(app_id, std::move(icon_key), icon_compression,
-                         size_hint_in_dip, std::move(callback));
+  iter->second->LoadIcon(std::move(icon_key), icon_compression,
+                         size_hint_in_dip, allow_placeholder_icon,
+                         std::move(callback));
 }
 
 void AppServiceImpl::Launch(apps::mojom::AppType app_type,
diff --git a/chrome/services/app_service/app_service_impl.h b/chrome/services/app_service/app_service_impl.h
index b9f239b..8984481 100644
--- a/chrome/services/app_service/app_service_impl.h
+++ b/chrome/services/app_service/app_service_impl.h
@@ -32,10 +32,10 @@
   void RegisterSubscriber(apps::mojom::SubscriberPtr subscriber,
                           apps::mojom::ConnectOptionsPtr opts) override;
   void LoadIcon(apps::mojom::AppType app_type,
-                const std::string& app_id,
                 apps::mojom::IconKeyPtr icon_key,
                 apps::mojom::IconCompression icon_compression,
                 int32_t size_hint_in_dip,
+                bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
   void Launch(apps::mojom::AppType app_type,
               const std::string& app_id,
diff --git a/chrome/services/app_service/app_service_impl_unittest.cc b/chrome/services/app_service/app_service_impl_unittest.cc
index 529a8e8..50d38f98 100644
--- a/chrome/services/app_service/app_service_impl_unittest.cc
+++ b/chrome/services/app_service/app_service_impl_unittest.cc
@@ -37,7 +37,7 @@
     }
   }
 
-  std::string load_icon_app_id_;
+  std::string load_icon_s_key;
 
  private:
   void Connect(apps::mojom::SubscriberPtr subscriber,
@@ -46,12 +46,12 @@
     subscribers_.AddPtr(std::move(subscriber));
   }
 
-  void LoadIcon(const std::string& app_id,
-                apps::mojom::IconKeyPtr icon_key,
+  void LoadIcon(apps::mojom::IconKeyPtr icon_key,
                 apps::mojom::IconCompression icon_compression,
                 int32_t size_hint_in_dip,
+                bool allow_placeholder_icon,
                 LoadIconCallback callback) override {
-    load_icon_app_id_ = app_id;
+    load_icon_s_key = icon_key->s_key;
     std::move(callback).Run(apps::mojom::IconValue::New());
   }
 
@@ -193,20 +193,24 @@
                            : apps::mojom::AppType::kUnknown;
 
     bool callback_ran = false;
-    pub0.load_icon_app_id_ = "-";
-    pub1.load_icon_app_id_ = "-";
-    pub2.load_icon_app_id_ = "-";
+    pub0.load_icon_s_key = "-";
+    pub1.load_icon_s_key = "-";
+    pub2.load_icon_s_key = "-";
+    auto icon_key = apps::mojom::IconKey::New();
+    icon_key->s_key = "o";
+    constexpr bool allow_placeholder_icon = false;
     impl.LoadIcon(
-        app_type, "o", apps::mojom::IconKey::New(),
+        app_type, std::move(icon_key),
         apps::mojom::IconCompression::kUncompressed, size_hint_in_dip,
+        allow_placeholder_icon,
         base::BindOnce(
             [](bool* ran, apps::mojom::IconValuePtr iv) { *ran = true; },
             &callback_ran));
     base::RunLoop().RunUntilIdle();
     EXPECT_TRUE(callback_ran);
-    EXPECT_EQ("-", pub0.load_icon_app_id_);
-    EXPECT_EQ(i == 0 ? "o" : "-", pub1.load_icon_app_id_);
-    EXPECT_EQ("-", pub2.load_icon_app_id_);
+    EXPECT_EQ("-", pub0.load_icon_s_key);
+    EXPECT_EQ(i == 0 ? "o" : "-", pub1.load_icon_s_key);
+    EXPECT_EQ("-", pub2.load_icon_s_key);
   }
 }
 
diff --git a/chrome/services/app_service/public/mojom/app_service.mojom b/chrome/services/app_service/public/mojom/app_service.mojom
index 9cd98937..c957fe5 100644
--- a/chrome/services/app_service/public/mojom/app_service.mojom
+++ b/chrome/services/app_service/public/mojom/app_service.mojom
@@ -21,10 +21,10 @@
   // App Icon Factory methods.
   LoadIcon(
       AppType app_type,
-      string app_id,
       IconKey icon_key,
       IconCompression icon_compression,
-      int32 size_hint_in_dip) => (IconValue icon_value);
+      int32 size_hint_in_dip,
+      bool allow_placeholder_icon) => (IconValue icon_value);
 
   // App Runner methods.
   Launch(
@@ -54,10 +54,10 @@
 
   // App Icon Factory methods.
   LoadIcon(
-      string app_id,
       IconKey icon_key,
       IconCompression icon_compression,
-      int32 size_hint_in_dip) => (IconValue icon_value);
+      int32 size_hint_in_dip,
+      bool allow_placeholder_icon) => (IconValue icon_value);
 
   // App Runner methods.
   Launch(
diff --git a/chrome/services/app_service/public/mojom/types.mojom b/chrome/services/app_service/public/mojom/types.mojom
index 237a99a..6fd7f39 100644
--- a/chrome/services/app_service/public/mojom/types.mojom
+++ b/chrome/services/app_service/public/mojom/types.mojom
@@ -108,6 +108,7 @@
   IconCompression icon_compression;
   gfx.mojom.ImageSkia? uncompressed;
   array<uint8>? compressed;
+  bool is_placeholder_icon;
 };
 
 enum LaunchSource {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 86bc8b3d..742fd02b 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -5471,6 +5471,7 @@
       "//components/sync:test_support_testserver",
       "//content/test:test_support",
       "//net",
+      "//services/identity/public/cpp:test_support",
       "//skia",
     ]
 
diff --git a/chrome/test/base/js2gtest.js b/chrome/test/base/js2gtest.js
index fa35d87..bd16af8 100644
--- a/chrome/test/base/js2gtest.js
+++ b/chrome/test/base/js2gtest.js
@@ -187,7 +187,7 @@
     if (this[testFixture].prototype.commandLineSwitches)
       output('#include "base/command_line.h"');
     if (this[testFixture].prototype.featureList ||
-        this[testFixture].prototype.featureWithParameters)
+        this[testFixture].prototype.featuresWithParameters)
       output('#include "base/test/scoped_feature_list.h"');
   }
   output();
@@ -395,9 +395,9 @@
     var switches = this[testFixture].prototype.commandLineSwitches;
     var hasSwitches = switches && switches.length;
     var featureList = this[testFixture].prototype.featureList;
-    var featureWithParameters =
-        this[testFixture].prototype.featureWithParameters;
-    if ((!hasSwitches && !featureList && !featureWithParameters) ||
+    var featuresWithParameters =
+        this[testFixture].prototype.featuresWithParameters;
+    if ((!hasSwitches && !featureList && !featuresWithParameters) ||
         typedefCppFixture == 'V8UnitTest') {
       output(`
 typedef ${typedefCppFixture} ${testFixture};
@@ -407,7 +407,7 @@
       output(`
 class ${testFixture} : public ${typedefCppFixture} {
  protected:`);
-      if (featureList || featureWithParameters) {
+      if (featureList || featuresWithParameters) {
         output(`
   ${testFixture}() {`);
         if (featureList) {
@@ -415,20 +415,23 @@
     scoped_feature_list_.InitWithFeatures({${featureList[0]}},
                                           {${featureList[1]}});`);
         }
-        if (featureWithParameters) {
-          var feature = featureWithParameters[0];
-          var parameters = featureWithParameters[1];
+        if (featuresWithParameters) {
+          for (var i = 0; i < featuresWithParameters.length; ++i) {
+            var feature = featuresWithParameters[i];
+            var featureName = feature[0];
+            var parameters = feature[1];
           output(`
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        ${feature}, {`);
-          for (var parameter of parameters) {
-            var parameterName = parameter[0];
-            var parameterValue = parameter[1];
-            output(`
+    scoped_feature_list${i}_.InitAndEnableFeatureWithParameters(
+        ${featureName}, {`);
+            for (var parameter of parameters) {
+              var parameterName = parameter[0];
+              var parameterValue = parameter[1];
+              output(`
             {"${parameterName}", "${parameterValue}"},`);
-          }
-          output(`
+            }
+            output(`
     });`);
+          }
         }
         output(`
   }`);
@@ -453,9 +456,17 @@
       output(`
   }`);
       }
-      if (featureList || featureWithParameters) {
+      if (featureList || featuresWithParameters) {
+        if (featureList) {
         output(`
   base::test::ScopedFeatureList scoped_feature_list_;`);
+        }
+        if (featuresWithParameters) {
+          for (var i = 0; i < featuresWithParameters.length; ++i) {
+            output(`
+  base::test::ScopedFeatureList scoped_feature_list${i}_;`);
+          }
+        }
       }
       output(`
 };
diff --git a/chrome/test/chromedriver/client/chromedriver.py b/chrome/test/chromedriver/client/chromedriver.py
index c264918..bc81d5898c 100644
--- a/chrome/test/chromedriver/client/chromedriver.py
+++ b/chrome/test/chromedriver/client/chromedriver.py
@@ -150,7 +150,13 @@
             chrome_processes = chromedriver_process.children()
             if len(chrome_processes) == 1:
               # Remove core file size limit, then use SIGABRT to dump core.
-              chrome_processes[0].rlimit(
+              # Newer versions of psutil.Process have rlimit method, while older
+              # versions have set_rlimit method.
+              if hasattr(chrome_processes[0], 'rlimit'):
+                rlimit_method = chrome_processes[0].rlimit
+              else:
+                rlimit_method = chrome_processes[0].set_rlimit
+              rlimit_method(
                   psutil.RLIMIT_CORE,
                   (psutil.RLIM_INFINITY, psutil.RLIM_INFINITY))
               chrome_processes[0].send_signal(signal.SIGABRT)
diff --git a/chrome/test/data/extensions/api_test/content_scripts/messaging/background.js b/chrome/test/data/extensions/api_test/content_scripts/messaging/background.js
new file mode 100644
index 0000000..c6a3506
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/messaging/background.js
@@ -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.
+
+chrome.test.getConfig(function(config) {
+  var testUrl =
+      `http://localhost:${config.testServer.port}/extensions/test_file.html`;
+  chrome.tabs.create({url: testUrl});
+});
diff --git a/chrome/test/data/extensions/api_test/content_scripts/messaging/content_script.js b/chrome/test/data/extensions/api_test/content_scripts/messaging/content_script.js
new file mode 100644
index 0000000..534889e
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/messaging/content_script.js
@@ -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.
+
+var TEST_MESSAGE = 'test_message';
+
+function assertValidExternalMessageSender(sender) {
+  chrome.test.assertEq(chrome.runtime.id, sender.id);
+  chrome.test.assertEq(undefined, sender.tlsChannelId);
+  // TODO(crbug.com/931962): Enable after the API implementation gets fixed:
+  // chrome.test.assertEq(undefined, sender.tab);
+  // chrome.test.assertEq(undefined, sender.frameId);
+  // chrome.test.assertEq(undefined, sender.url);
+}
+
+chrome.test.runTests([
+  // Test that the API that allows to directly communicate with other content
+  // scripts is unavailable.
+  function testNoTabsMessagingApi() {
+    chrome.test.assertTrue(!chrome.tabs);
+    chrome.test.succeed();
+  },
+  // Test that we can successfully send a message to an extension that doesn't
+  // have custom externally_connectable manifest property.
+  function testMessageToExtensionAllowingByDefault() {
+    chrome.runtime.sendMessage(
+        'badpbjaedophlnacllhobhnbcgomhbcd',
+        TEST_MESSAGE,
+        function(response) {
+          chrome.test.assertEq(TEST_MESSAGE, response.receivedMessage);
+          assertValidExternalMessageSender(response.receivedSender);
+          chrome.test.succeed();
+        });
+  },
+  // Test that we can successfully send a message to an extension that specifies
+  // our extension ID in its externally_connectable manifest property.
+  function testMessageToAllowingExtension() {
+    chrome.runtime.sendMessage(
+        'pmnfaklgffejbafjijfofbcianldmhci',
+        TEST_MESSAGE,
+        function(response) {
+          chrome.test.assertEq(TEST_MESSAGE, response.receivedMessage);
+          assertValidExternalMessageSender(response.receivedSender);
+          chrome.test.succeed();
+        });
+  },
+  // Test that we cannot send a message to an extension that has custom
+  // externally_connectable manifest property, but doesn't contain our extension
+  // ID there.
+  function testMessageToDenyingExtension() {
+    chrome.runtime.sendMessage(
+        'gcdagggcealpldjgchchljhjlhikcmco',
+        TEST_MESSAGE,
+        function(response) {
+          chrome.test.assertLastError('Could not establish connection. ' +
+                                      'Receiving end does not exist.');
+          chrome.test.succeed();
+        });
+  },
+]);
diff --git a/chrome/test/data/extensions/api_test/content_scripts/messaging/manifest.json b/chrome/test/data/extensions/api_test/content_scripts/messaging/manifest.json
new file mode 100644
index 0000000..ea44c723
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/messaging/manifest.json
@@ -0,0 +1,17 @@
+{
+  "name": "content_script_messaging (ID iehnoephgajegjjclojodjindjofbkcb)",
+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzdFXpwj4YHkWuwNA6fp+7GdrSjXcDvDxTPWNeQ1rfINDvirEZ6a1GRi/i29I0mW9xFNCQ7zaM/EGWqDfyEoWg2AHxGEK4VXA1aseQFNfhF7oGPWCu8v9QdwI940TTSN7rZWHj2GezTzToTl9QJ5VKekYf5wWlnbZ1t60OuWlPRxKVEeG5a8JPYpkFcOCuxfAm4fJ+es0lYBsSDPqCOTNyBBTQmuh9hoAfBaU1LZzbWqFfJUITHx9N7qNiC1ksGuJQ4zn8xhRNngSKO/L0I7am9/J8imA3aW6H7eLmVJxgRG0USCwlmR+1rsDepD9gzy4bmf9GcOh3C2qVVC4FvttXQIDAQAB",
+  "version": "1.0",
+  "manifest_version": 2,
+  "description": "Tests the message passing behavior from a content script.",
+  "background": {
+    "persistent": true,
+    "scripts": ["background.js"]
+  },
+  "content_scripts": [
+    {
+      "matches": ["http://localhost:*/extensions/test_file.html"],
+      "js": ["content_script.js"]
+    }
+  ]
+}
diff --git a/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_allows/background.js b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_allows/background.js
new file mode 100644
index 0000000..3c30766
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_allows/background.js
@@ -0,0 +1,8 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.runtime.onMessageExternal.addListener(
+    function(message, sender, sendResponse) {
+      sendResponse({receivedMessage: message, receivedSender: sender});
+    });
diff --git a/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_allows/manifest.json b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_allows/manifest.json
new file mode 100644
index 0000000..3280542
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_allows/manifest.json
@@ -0,0 +1,9 @@
+{
+  "name": "content_script_message_echoer_allows (ID pmnfaklgffejbafjijfofbcianldmhci)",
+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp2mL/rBp2YqOLYcBkXOPdsONV3q13ra7IpnBu4c5v6bzbZ98FljOoBPhxOGHCEIC9xAaENLwQjV0dfVmbJhKJFmEj/HylOZW1t/YZfO7EMqFjOk2eJ6bH2tVHIJpH5jR1LJxpAsJJkkaG7H7eHBMl9FO96Scrs5js+t9WpuDRK8vBBU/6bl7nGhvvHHVn6EjasLXf3Dk/D/0pBmXcqkyF/PXl0qQjguwd32nSipqZJQiepnL47gie2ILLC/87aa7rntk+j1i59y5tFMb+OGtbh930phP68oARgRvBPrVKQ7npn22Bwn5Bt4nmeT41P2k0MGG3wklCEzlv9vfs+eDdQIDAQAB",
+  "version": "1.0",
+  "manifest_version": 2,
+  "description": "Is used in tests to verify that a content script can talk to an extension that has it listed in the external_connectible value.",
+  "background": { "scripts": ["background.js"] },
+  "externally_connectable": {"ids": ["iehnoephgajegjjclojodjindjofbkcb"]}
+}
diff --git a/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_allows_by_default/background.js b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_allows_by_default/background.js
new file mode 100644
index 0000000..3c30766
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_allows_by_default/background.js
@@ -0,0 +1,8 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.runtime.onMessageExternal.addListener(
+    function(message, sender, sendResponse) {
+      sendResponse({receivedMessage: message, receivedSender: sender});
+    });
diff --git a/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_allows_by_default/manifest.json b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_allows_by_default/manifest.json
new file mode 100644
index 0000000..ab18c55
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_allows_by_default/manifest.json
@@ -0,0 +1,8 @@
+{
+  "name": "content_script_message_echoer_allows_by_default (ID badpbjaedophlnacllhobhnbcgomhbcd)",
+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp3+k29aqv3R+3awyoTtSKln7fJhzsmn1u9RD47fixNZW8PPweiuRKmg30Ctm2hrB0y+UU0EJpUkjSPqxUkW+2NnNAABhsdb75BMJffuQhTyOJGw60IPTLE31kHOoGsSS6wTUGI6+qUS/3PyTL+/eW+pHlGgxH4+Zjyp8UgdOIaitbKwDL6kJjXXiCBx3RmhyLr+xPSHfzrg/BQ8bQfNMOt2s0leINuBds605BJjWXZ6Yu3D96jecSCy1C7MtV4+WY6f/4TLCD0ZhmQhYsvOKZ9hPC6pV3Ui1KDqeVp58JZfsPP9/bpVqz1jdg384wJs4QXjrZNzHxRKDBPw/G1EeuQIDAQAB",
+  "version": "1.0",
+  "manifest_version": 2,
+  "description": "Is used in tests to verify that a content script can talk to an extension that has no custom external_connectible value.",
+  "background": { "scripts": ["background.js"] }
+}
diff --git a/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_denies/background.js b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_denies/background.js
new file mode 100644
index 0000000..3c30766
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_denies/background.js
@@ -0,0 +1,8 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.runtime.onMessageExternal.addListener(
+    function(message, sender, sendResponse) {
+      sendResponse({receivedMessage: message, receivedSender: sender});
+    });
diff --git a/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_denies/manifest.json b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_denies/manifest.json
new file mode 100644
index 0000000..2c4ea37
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/message_echoer_denies/manifest.json
@@ -0,0 +1,9 @@
+{
+  "name": "content_script_message_echoer_denies (ID gcdagggcealpldjgchchljhjlhikcmco)",
+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArQY26TQt78EYprMLn1l2zanD6RNSVG6Eky7bIzg6qpNEt/fT9ghIHAjs0SxpcCnhEfGPbNvA80fmK9Lq0eMBcqW76JA99sFwiG/WgyLDnAuiabuMJ56WNoNi37xjbPjPvLQHGb+06fny6s+SMpm76+B/hMK8BvySd7d/EOgcFxSXNrhWwxPXKx2uaFU6gza+rqCiVxHt/pjoKaas1mTlvPsOiW6DPvtgDujxpPUum5RmusUa8c/jtPFY0Rhe32h7NoxJtYD6nDGC1KzJ9lHaPsNtjGaFdyhRihyMtr+IVK68WnelTI9GYLJU1fbrfIYBlP0D2dTDH8sg+iiVj4BKMwIDAQAB",
+  "version": "1.0",
+  "manifest_version": 2,
+  "description": "Is used in tests to verify that a content script can NOT talk to an extension that has it missing from the non-defaulted external_connectible value.",
+  "background": { "scripts": ["background.js"] },
+  "externally_connectable": {"ids": []}
+}
diff --git a/chrome/test/data/extensions/api_test/declarative_net_request/dynamic_rules/background.js b/chrome/test/data/extensions/api_test/declarative_net_request/dynamic_rules/background.js
new file mode 100644
index 0000000..5be1ee86
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/declarative_net_request/dynamic_rules/background.js
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var callbackFail = chrome.test.callbackFail;
+
+var emptyCallback = function() {};
+var expectedError = 'Not implemented';
+
+chrome.test.runTests([
+  function addDynamicRules() {
+    chrome.declarativeNetRequest.addDynamicRules(
+        [], callbackFail(expectedError, emptyCallback));
+  },
+  function removeDynamicRules() {
+    chrome.declarativeNetRequest.removeDynamicRules(
+        [], callbackFail(expectedError, emptyCallback));
+  },
+  function getDynamicRules() {
+    chrome.declarativeNetRequest.getDynamicRules(
+        callbackFail(expectedError, function(rules) {}));
+  }
+]);
diff --git a/chrome/test/data/extensions/api_test/declarative_net_request/dynamic_rules/manifest.json b/chrome/test/data/extensions/api_test/declarative_net_request/dynamic_rules/manifest.json
new file mode 100644
index 0000000..b494386
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/declarative_net_request/dynamic_rules/manifest.json
@@ -0,0 +1,18 @@
+{
+  "name": "Test extension",
+  "declarative_net_request": {
+    "rule_resources": [
+      "rules_file_empty.json"
+    ]
+  },
+  "manifest_version": 2,
+  "permissions": [
+    "declarativeNetRequest"
+  ],
+  "version": "1.0",
+  "background": {
+    "scripts": [
+      "background.js"
+    ]
+  }
+}
diff --git a/chrome/test/data/extensions/api_test/declarative_net_request/dynamic_rules/rules_file_empty.json b/chrome/test/data/extensions/api_test/declarative_net_request/dynamic_rules/rules_file_empty.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/declarative_net_request/dynamic_rules/rules_file_empty.json
@@ -0,0 +1 @@
+[]
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index e13e316c..c37e6d9 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -56,6 +56,7 @@
     "cr_elements/cr_elements_browsertest.js",
     "find_shortcut_behavior_browsertest.js",
     "history/history_browsertest.js",
+    "js2gtest_browsertest.js",
     "load_time_data_browsertest.js",
     "media_router/media_router_elements_browsertest.js",
     "mock4js_browsertest.js",
diff --git a/chrome/test/data/webui/app_management/app_management_browsertest.js b/chrome/test/data/webui/app_management/app_management_browsertest.js
index bed944d..1f22924 100644
--- a/chrome/test/data/webui/app_management/app_management_browsertest.js
+++ b/chrome/test/data/webui/app_management/app_management_browsertest.js
@@ -19,7 +19,9 @@
   browsePreload: 'chrome://apps',
 
   extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+    '../test_store.js',
     'test_util.js',
+    'test_store.js',
   ]),
 
   featureList: ['features::kAppManagement', ''],
@@ -98,6 +100,20 @@
   mocha.run();
 });
 
+function AppManagementRouterTest() {}
+
+AppManagementRouterTest.prototype = {
+  __proto__: AppManagementBrowserTest.prototype,
+
+  extraLibraries: AppManagementBrowserTest.prototype.extraLibraries.concat([
+    'router_test.js',
+  ]),
+};
+
+TEST_F('AppManagementRouterTest', 'All', function() {
+  mocha.run();
+});
+
 function AppManagementPwaPermissionViewTest() {}
 
 AppManagementPwaPermissionViewTest.prototype = {
diff --git a/chrome/test/data/webui/app_management/main_view_test.js b/chrome/test/data/webui/app_management/main_view_test.js
index 379591e..f14d9c0 100644
--- a/chrome/test/data/webui/app_management/main_view_test.js
+++ b/chrome/test/data/webui/app_management/main_view_test.js
@@ -21,13 +21,11 @@
   setup(function() {
     appIdCounter = 0;
 
-    mainView = document.createElement('app-management-main-view');
-    PolymerTest.clearBody();
-
     fakeHandler = setupFakeHandler();
     replaceStore();
 
-    document.body.appendChild(mainView);
+    mainView = document.createElement('app-management-main-view');
+    replaceBody(mainView);
   });
 
   test('simple app addition', async function() {
diff --git a/chrome/test/data/webui/app_management/metadata_view_test.js b/chrome/test/data/webui/app_management/metadata_view_test.js
index c337bc5f..8c18b4a 100644
--- a/chrome/test/data/webui/app_management/metadata_view_test.js
+++ b/chrome/test/data/webui/app_management/metadata_view_test.js
@@ -11,9 +11,6 @@
   const APP_ID = '1';
 
   setup(async function() {
-    metadataView = document.createElement('app-management-metadata-view');
-
-    PolymerTest.clearBody();
     fakeHandler = setupFakeHandler();
     replaceStore();
 
@@ -22,7 +19,8 @@
     app_management.Store.getInstance().dispatch(
         app_management.actions.changePage(PageType.DETAIL, APP_ID));
 
-    document.body.appendChild(metadataView);
+    metadataView = document.createElement('app-management-metadata-view');
+    replaceBody(metadataView);
   });
 
   test(
diff --git a/chrome/test/data/webui/app_management/pwa_permission_view_test.js b/chrome/test/data/webui/app_management/pwa_permission_view_test.js
index c69f3979..93198f2 100644
--- a/chrome/test/data/webui/app_management/pwa_permission_view_test.js
+++ b/chrome/test/data/webui/app_management/pwa_permission_view_test.js
@@ -27,10 +27,6 @@
   }
 
   setup(async function() {
-    pwaPermissionView =
-        document.createElement('app-management-pwa-permission-view');
-    PolymerTest.clearBody();
-
     fakeHandler = setupFakeHandler();
     replaceStore();
 
@@ -38,7 +34,9 @@
     await fakeHandler.addApp(TEST_APP_ID);
     app_management.Store.getInstance().dispatch(
         app_management.actions.changePage(PageType.DETAIL, TEST_APP_ID));
-    document.body.appendChild(pwaPermissionView);
+    pwaPermissionView =
+        document.createElement('app-management-pwa-permission-view');
+    replaceBody(pwaPermissionView);
   });
 
   test('App is rendered correctly', function() {
diff --git a/chrome/test/data/webui/app_management/reducers_test.js b/chrome/test/data/webui/app_management/reducers_test.js
index 9ba3f21..bdb1040 100644
--- a/chrome/test/data/webui/app_management/reducers_test.js
+++ b/chrome/test/data/webui/app_management/reducers_test.js
@@ -6,6 +6,7 @@
 
 suite('app state', function() {
   let apps;
+  let state;
 
   setup(function() {
     apps = {
diff --git a/chrome/test/data/webui/app_management/router_test.js b/chrome/test/data/webui/app_management/router_test.js
new file mode 100644
index 0000000..a03bcc1
--- /dev/null
+++ b/chrome/test/data/webui/app_management/router_test.js
@@ -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.
+
+suite('<app-management-router>', function() {
+  let store;
+  let router;
+  let fakeHandler;
+
+  async function navigateTo(route) {
+    window.history.replaceState({}, '', route);
+    window.dispatchEvent(new CustomEvent('location-changed'));
+    await PolymerTest.flushTasks();
+  }
+
+  function getCurrentUrlSuffix() {
+    return window.location.href.slice(window.location.origin.length);
+  }
+
+  setup(async function() {
+    fakeHandler = setupFakeHandler();
+    store = new app_management.TestStore();
+    await fakeHandler.addApp('1');
+    store.replaceSingleton();
+    router = document.createElement('app-management-router');
+    replaceBody(router);
+  });
+
+  test('search updates from route', async function() {
+    await navigateTo('/?q=beep');
+    const expected = app_management.actions.setSearchTerm('beep');
+    assertDeepEquals(expected, store.lastAction);
+  });
+
+  test('selected app updates from route', async function() {
+    await navigateTo('/detail?id=1');
+    const expected = app_management.actions.changePage(PageType.DETAIL, '1');
+
+    assertDeepEquals(expected, store.lastAction);
+  });
+
+  test('notifications view appears from route', async function() {
+    await navigateTo('/notifications');
+    const expected = app_management.actions.changePage(PageType.NOTIFICATIONS);
+    assertDeepEquals(expected, store.lastAction);
+  });
+
+  test('route updates from state change', async function() {
+    // The application needs an initial url to start with.
+    await navigateTo('/');
+
+    store.data.currentPage = {
+      pageType: PageType.DETAIL,
+      selectedAppId: '1',
+    };
+    store.notifyObservers();
+
+    await PolymerTest.flushTasks();
+    assertEquals('/detail?id=1', getCurrentUrlSuffix());
+
+    // Returning main page clears the route.
+    store.data.currentPage = {
+      pageType: PageType.MAIN,
+      selectedAppId: null,
+    };
+    store.notifyObservers();
+    await PolymerTest.flushTasks();
+    assertEquals('/', getCurrentUrlSuffix());
+
+    store.data.currentPage = {
+      pageType: PageType.NOTIFICATIONS,
+      selectedAppId: null,
+    };
+    store.notifyObservers();
+    await PolymerTest.flushTasks();
+    assertEquals('/notifications', getCurrentUrlSuffix());
+  });
+
+  test('route updates from search', async function() {
+    await navigateTo('/');
+    store.data.search = {term: 'bloop'};
+    store.notifyObservers();
+    await PolymerTest.flushTasks();
+
+    assertEquals('/?q=bloop', getCurrentUrlSuffix());
+  });
+});
diff --git a/chrome/test/data/webui/app_management/test_store.js b/chrome/test/data/webui/app_management/test_store.js
new file mode 100644
index 0000000..159af67
--- /dev/null
+++ b/chrome/test/data/webui/app_management/test_store.js
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+suiteSetup(function() {
+  cr.define('app_management', function() {
+    class TestStore extends cr.ui.TestStore {
+      constructor(data) {
+        super(
+            data, app_management.Store, app_management.util.createEmptyState(),
+            app_management.reduceAction);
+      }
+    }
+
+    return {
+      TestStore: TestStore,
+    };
+  });
+});
diff --git a/chrome/test/data/webui/app_management/test_util.js b/chrome/test/data/webui/app_management/test_util.js
index f0482197..703cb32 100644
--- a/chrome/test/data/webui/app_management/test_util.js
+++ b/chrome/test/data/webui/app_management/test_util.js
@@ -44,3 +44,25 @@
   const rect = element.getBoundingClientRect();
   return rect.height === 0 && rect.width === 0;
 }
+
+/**
+ * Create an app for testing purpose.
+ * @param {string} id
+ * @param {Object=} optConfig
+ * @return {!App}
+ */
+function createApp(id, config) {
+  return app_management.FakePageHandler.createApp(id, config);
+}
+
+/**
+ * Replace the current body of the test with a new element.
+ * @param {Element} element
+ */
+function replaceBody(element) {
+  PolymerTest.clearBody();
+
+  window.history.replaceState({}, '', '/');
+
+  document.body.appendChild(element);
+}
diff --git a/chrome/test/data/webui/js2gtest_browsertest.js b/chrome/test/data/webui/js2gtest_browsertest.js
new file mode 100644
index 0000000..b060cec
--- /dev/null
+++ b/chrome/test/data/webui/js2gtest_browsertest.js
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+GEN(`
+#include "base/metrics/field_trial_params.h"
+
+const base::Feature kTestFeature{"TestFeature",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kTestFeatureWithParam{"TestFeatureWithParam",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+const base::FeatureParam<int> kTestFeatureWithParamCount{
+    &kTestFeatureWithParam, "count", 5};
+`);
+
+/**
+ * @constructor
+ * @extends {testing.Test}
+ */
+function JSToGtestBrowserTest() {}
+
+JSToGtestBrowserTest.prototype = {
+  __proto__: testing.Test.prototype,
+
+  browsePreload: 'chrome://dummyurl',
+
+  /** @override */
+  testGenPostamble() {
+    GEN(`
+  EXPECT_TRUE(base::FeatureList::IsEnabled(kTestFeature));
+  EXPECT_TRUE(base::FeatureList::IsEnabled(kTestFeatureWithParam));
+  EXPECT_EQ(5,
+      base::GetFieldTrialParamByFeatureAsInt(kTestFeatureWithParam,
+      kTestFeatureWithParamCount.name, 0));`);
+  },
+
+  /** @override */
+  featureList: ['kTestFeature', ''],
+
+  /** @override */
+  featuresWithParameters: [
+    ['kTestFeatureWithParam', [['count', '5']]],
+  ],
+};
+
+TEST_F('JSToGtestBrowserTest', 'TestFeatureEnabling', function() {
+  // Just to be generated.
+});
diff --git a/chrome/test/data/webui/print_preview/pages_settings_test.js b/chrome/test/data/webui/print_preview/pages_settings_test.js
index 11012e1..a82f06b 100644
--- a/chrome/test/data/webui/print_preview/pages_settings_test.js
+++ b/chrome/test/data/webui/print_preview/pages_settings_test.js
@@ -244,8 +244,8 @@
 
     // Tests that the clearing a valid input has no effect, clearing an invalid
     // input does not show an error message but does not reset the preview, and
-    // changing focus from an empty input in either case automatically reselects
-    // the "all" radio button.
+    // changing focus from an empty input in either case fills in the dropdown
+    // with the full page range.
     test(assert(TestNames.ClearInput), function() {
       const input = pagesSection.$.pageSettingsCustomInput.inputElement;
       const select = pagesSection.$$('select');
@@ -266,28 +266,56 @@
             return whenBlurred;
           })
           .then(function() {
-            // Blurring does not change the state.
+            // Blurring a blank field sets the full page range.
             assertEquals(customValue, select.value);
-            validateState([1, 2], '', false);
-            assertEquals('1-2', input.value);
+            validateState([1, 2, 3], '', false);
+            assertEquals('1-3', input.value);
             return setupInput('5', 3);
           })
           .then(function() {
             assertEquals(customValue, select.value);
-            validateState([1, 2], limitError + '3', true);
+            // Invalid input doesn't change the preview.
+            validateState([1, 2, 3], limitError + '3', true);
             return setupInput('', 3);
           })
           .then(function() {
             assertEquals(customValue, select.value);
-            validateState([1, 2], '', false);
+            validateState([1, 2, 3], '', false);
             const whenBlurred = test_util.eventToPromise('blur', input);
             input.blur();
+            // Blurring an invalid value that has been cleared should reset the
+            // value to all pages.
             return whenBlurred;
           })
           .then(function() {
             assertEquals(customValue, select.value);
-            validateState([1, 2], '', false);
-            assertEquals('1-2', input.value);
+            validateState([1, 2, 3], '', false);
+            assertEquals('1-3', input.value);
+
+            // Clear the input and then select "All" in the dropdown.
+            input.focus();
+            return setupInput('', 3);
+          })
+          .then(function() {
+            select.focus();
+            select.value = pagesSection.pagesValueEnum_.ALL.toString();
+            select.dispatchEvent(new CustomEvent('change'));
+            return test_util.eventToPromise(
+                'process-select-change', pagesSection);
+          })
+          .then(function() {
+            Polymer.dom.flush();
+            assertEquals(allValue, select.value);
+            validateState([1, 2, 3], '', false);
+            // Reselect custom.
+            select.value = pagesSection.pagesValueEnum_.CUSTOM.toString();
+            select.dispatchEvent(new CustomEvent('change'));
+            return test_util.eventToPromise('focus', input);
+          })
+          .then(function() {
+            // Input has been cleared.
+            assertEquals('', input.value);
+            validateState([1, 2, 3], '', false);
           });
     });
 
diff --git a/chrome/test/data/webui/welcome/nux_ntp_background_test.js b/chrome/test/data/webui/welcome/nux_ntp_background_test.js
index bb08006..5073adcc 100644
--- a/chrome/test/data/webui/welcome/nux_ntp_background_test.js
+++ b/chrome/test/data/webui/welcome/nux_ntp_background_test.js
@@ -10,11 +10,13 @@
         id: 0,
         title: 'Cat',
         imageUrl: 'some/cute/photo/of/a/cat',
+        thumbnailClass: 'cat',
       },
       {
         id: 1,
         title: 'Venice',
         imageUrl: 'some/scenic/photo/of/a/beach',
+        thumbnailClass: 'venice',
       },
     ];
 
diff --git a/chromecast/browser/cast_browser_context.cc b/chromecast/browser/cast_browser_context.cc
index 9827c6da..950d8e5 100644
--- a/chromecast/browser/cast_browser_context.cc
+++ b/chromecast/browser/cast_browser_context.cc
@@ -5,8 +5,11 @@
 #include "chromecast/browser/cast_browser_context.h"
 
 #include <memory>
+#include <utility>
 
+#include "base/barrier_closure.h"
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/files/file_util.h"
 #include "base/macros.h"
 #include "base/path_service.h"
@@ -16,21 +19,26 @@
 #include "chromecast/browser/cast_permission_manager.h"
 #include "chromecast/browser/url_request_context_factory.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/cors_origin_pattern_setter.h"
 #include "content/public/browser/resource_context.h"
+#include "content/public/browser/shared_cors_origin_access_list.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_switches.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/features.h"
 
 namespace chromecast {
 namespace shell {
 
 namespace {
 const void* const kDownloadManagerDelegateKey = &kDownloadManagerDelegateKey;
-}
+}  // namespace
 
-class CastBrowserContext::CastResourceContext :
-    public content::ResourceContext {
+using content::CorsOriginPatternSetter;
+
+class CastBrowserContext::CastResourceContext
+    : public content::ResourceContext {
  public:
   CastResourceContext() {}
   ~CastResourceContext() override {}
@@ -42,17 +50,17 @@
 CastBrowserContext::CastBrowserContext(
     URLRequestContextFactory* url_request_context_factory)
     : url_request_context_factory_(url_request_context_factory),
-      resource_context_(new CastResourceContext) {
+      resource_context_(new CastResourceContext),
+      shared_cors_origin_access_list_(
+          content::SharedCorsOriginAccessList::Create()) {
   InitWhileIOAllowed();
 }
 
 CastBrowserContext::~CastBrowserContext() {
   BrowserContext::NotifyWillBeDestroyed(this);
   ShutdownStoragePartitions();
-  content::BrowserThread::DeleteSoon(
-      content::BrowserThread::IO,
-      FROM_HERE,
-      resource_context_.release());
+  content::BrowserThread::DeleteSoon(content::BrowserThread::IO, FROM_HERE,
+                                     resource_context_.release());
 }
 
 void CastBrowserContext::InitWhileIOAllowed() {
@@ -171,9 +179,45 @@
 
 net::URLRequestContextGetter*
 CastBrowserContext::CreateMediaRequestContextForStoragePartition(
-    const base::FilePath& partition_path, bool in_memory) {
+    const base::FilePath& partition_path,
+    bool in_memory) {
   return nullptr;
 }
 
+void CastBrowserContext::SetCorsOriginAccessListForOrigin(
+    const url::Origin& source_origin,
+    std::vector<network::mojom::CorsOriginPatternPtr> allow_patterns,
+    std::vector<network::mojom::CorsOriginPatternPtr> block_patterns,
+    base::OnceClosure closure) {
+  if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+    shared_cors_origin_access_list_->SetForOrigin(
+        source_origin, std::move(allow_patterns), std::move(block_patterns),
+        std::move(closure));
+  } else {
+    auto barrier_closure = BarrierClosure(2, std::move(closure));
+
+    // Keep profile storage partitions' NetworkContexts synchronized.
+    auto profile_setter = base::MakeRefCounted<CorsOriginPatternSetter>(
+        source_origin, CorsOriginPatternSetter::ClonePatterns(allow_patterns),
+        CorsOriginPatternSetter::ClonePatterns(block_patterns),
+        barrier_closure);
+    ForEachStoragePartition(
+        this, base::BindRepeating(&CorsOriginPatternSetter::SetLists,
+                                  base::RetainedRef(profile_setter.get())));
+
+    // Keep the per-profile access list up to date so that we can use this to
+    // restore NetworkContext settings at anytime, e.g. on restarting the
+    // network service.
+    shared_cors_origin_access_list_->SetForOrigin(
+        source_origin, std::move(allow_patterns), std::move(block_patterns),
+        barrier_closure);
+  }
+}
+
+const content::SharedCorsOriginAccessList*
+CastBrowserContext::GetSharedCorsOriginAccessList() const {
+  return shared_cors_origin_access_list_.get();
+}
+
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_browser_context.h b/chromecast/browser/cast_browser_context.h
index aa9d6896..f77f0d2 100644
--- a/chromecast/browser/cast_browser_context.h
+++ b/chromecast/browser/cast_browser_context.h
@@ -5,6 +5,8 @@
 #ifndef CHROMECAST_BROWSER_CAST_BROWSER_CONTEXT_H_
 #define CHROMECAST_BROWSER_CAST_BROWSER_CONTEXT_H_
 
+#include <vector>
+
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "content/public/browser/browser_context.h"
@@ -60,6 +62,14 @@
 
   net::URLRequestContextGetter* GetSystemRequestContext();
 
+  void SetCorsOriginAccessListForOrigin(
+      const url::Origin& source_origin,
+      std::vector<network::mojom::CorsOriginPatternPtr> allow_patterns,
+      std::vector<network::mojom::CorsOriginPatternPtr> block_patterns,
+      base::OnceClosure closure) override;
+  const content::SharedCorsOriginAccessList* GetSharedCorsOriginAccessList()
+      const override;
+
  private:
   class CastResourceContext;
 
@@ -71,6 +81,8 @@
   base::FilePath path_;
   std::unique_ptr<CastResourceContext> resource_context_;
   std::unique_ptr<content::PermissionControllerDelegate> permission_manager_;
+  scoped_refptr<content::SharedCorsOriginAccessList>
+      shared_cors_origin_access_list_;
 
   DISALLOW_COPY_AND_ASSIGN(CastBrowserContext);
 };
diff --git a/chromeos/components/drivefs/drivefs_host_unittest.cc b/chromeos/components/drivefs/drivefs_host_unittest.cc
index b3fd27f..5754435 100644
--- a/chromeos/components/drivefs/drivefs_host_unittest.cc
+++ b/chromeos/components/drivefs/drivefs_host_unittest.cc
@@ -433,6 +433,10 @@
 };
 
 TEST_F(DriveFsHostTest, Basic) {
+  MockDriveFsHostObserver observer;
+  ScopedObserver<DriveFsHost, DriveFsHostObserver> observer_scoper(&observer);
+  observer_scoper.Add(host_.get());
+
   EXPECT_FALSE(host_->IsMounted());
 
   EXPECT_EQ(base::FilePath("/path/to/profile/GCache/v2/salt-g-ID"),
@@ -444,7 +448,12 @@
   EXPECT_EQ(base::FilePath("/media/drivefsroot/salt-g-ID"),
             host_->GetMountPath());
 
-  DoUnmount();
+  EXPECT_CALL(observer, OnUnmounted());
+  EXPECT_CALL(*host_delegate_, OnUnmounted(_)).Times(0);
+  base::RunLoop run_loop;
+  delegate_ptr_.set_connection_error_handler(run_loop.QuitClosure());
+  host_->Unmount();
+  run_loop.Run();
 }
 
 TEST_F(DriveFsHostTest, GetMountPathWhileUnmounted) {
@@ -452,31 +461,6 @@
             host_->GetMountPath());
 }
 
-TEST_F(DriveFsHostTest, OnMountedBeforeMountEvent) {
-  auto token = StartMount();
-  ASSERT_TRUE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
-  SendOnMounted();
-  EXPECT_CALL(*host_delegate_, OnMounted(_)).Times(0);
-  delegate_ptr_.FlushForTesting();
-
-  testing::Mock::VerifyAndClear(host_delegate_.get());
-
-  EXPECT_FALSE(host_->IsMounted());
-
-  EXPECT_CALL(*host_delegate_,
-              OnMounted(base::FilePath("/media/drivefsroot/salt-g-ID")));
-  EXPECT_CALL(*disk_manager_, UnmountPath("/media/drivefsroot/salt-g-ID",
-                                          chromeos::UNMOUNT_OPTIONS_NONE, _));
-
-  DispatchMountSuccessEvent(token);
-
-  ASSERT_TRUE(host_->IsMounted());
-  EXPECT_EQ(base::FilePath("/media/drivefsroot/salt-g-ID"),
-            host_->GetMountPath());
-
-  DoUnmount();
-}
-
 TEST_F(DriveFsHostTest, OnMountFailedFromMojo) {
   ASSERT_FALSE(host_->IsMounted());
 
@@ -492,6 +476,7 @@
 
 TEST_F(DriveFsHostTest, OnMountFailedFromDbus) {
   ASSERT_FALSE(host_->IsMounted());
+  EXPECT_CALL(*disk_manager_, UnmountPath(_, _, _)).Times(0);
 
   auto token = StartMount();
 
@@ -508,79 +493,6 @@
   run_loop.Run();
 
   ASSERT_FALSE(host_->IsMounted());
-}
-
-TEST_F(DriveFsHostTest, OnMountFailed_UnmountInObserver) {
-  ASSERT_FALSE(host_->IsMounted());
-
-  auto token = StartMount();
-
-  base::RunLoop run_loop;
-  base::OnceClosure quit_closure = run_loop.QuitClosure();
-  EXPECT_CALL(*host_delegate_, OnMountFailed(MountFailure::kInvocation, _))
-      .WillOnce(testing::InvokeWithoutArgs([&]() {
-        std::move(quit_closure).Run();
-        host_->Unmount();
-      }));
-  DispatchMountEvent(chromeos::disks::DiskMountManager::MOUNTING,
-                     chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS,
-                     {base::StrCat({"drivefs://", token}),
-                      "/media/drivefsroot/salt-g-ID",
-                      chromeos::MOUNT_TYPE_NETWORK_STORAGE,
-                      {}});
-  run_loop.Run();
-
-  ASSERT_FALSE(host_->IsMounted());
-}
-
-TEST_F(DriveFsHostTest, UnmountAfterMountComplete) {
-  MockDriveFsHostObserver observer;
-  ScopedObserver<DriveFsHost, DriveFsHostObserver> observer_scoper(&observer);
-  observer_scoper.Add(host_.get());
-
-  ASSERT_NO_FATAL_FAILURE(DoMount());
-
-  EXPECT_CALL(observer, OnUnmounted());
-  base::RunLoop run_loop;
-  delegate_ptr_.set_connection_error_handler(run_loop.QuitClosure());
-  host_->Unmount();
-  run_loop.Run();
-}
-
-TEST_F(DriveFsHostTest, UnmountBeforeMountEvent) {
-  MockDriveFsHostObserver observer;
-  ScopedObserver<DriveFsHost, DriveFsHostObserver> observer_scoper(&observer);
-  observer_scoper.Add(host_.get());
-  EXPECT_CALL(observer, OnUnmounted()).Times(0);
-
-  auto token = StartMount();
-  EXPECT_FALSE(host_->IsMounted());
-  host_->Unmount();
-  EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
-}
-
-TEST_F(DriveFsHostTest, UnmountBeforeMojoConnection) {
-  MockDriveFsHostObserver observer;
-  ScopedObserver<DriveFsHost, DriveFsHostObserver> observer_scoper(&observer);
-  observer_scoper.Add(host_.get());
-  EXPECT_CALL(observer, OnUnmounted()).Times(0);
-
-  auto token = StartMount();
-  DispatchMountSuccessEvent(token);
-
-  EXPECT_FALSE(host_->IsMounted());
-  EXPECT_CALL(*disk_manager_, UnmountPath("/media/drivefsroot/salt-g-ID",
-                                          chromeos::UNMOUNT_OPTIONS_NONE, _));
-
-  host_->Unmount();
-  EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
-}
-
-TEST_F(DriveFsHostTest, DestroyBeforeMountEvent) {
-  auto token = StartMount();
-  EXPECT_CALL(*disk_manager_, UnmountPath(_, _, _)).Times(0);
-
-  host_.reset();
   EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
 }
 
@@ -594,93 +506,11 @@
   EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
 }
 
-TEST_F(DriveFsHostTest, ObserveOtherMount) {
-  auto token = StartMount();
-  EXPECT_CALL(*disk_manager_, UnmountPath(_, _, _)).Times(0);
-
-  DispatchMountEvent(chromeos::disks::DiskMountManager::MOUNTING,
-                     chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED,
-                     {"some/other/mount/event",
-                      "/some/other/mount/point",
-                      chromeos::MOUNT_TYPE_DEVICE,
-                      {}});
-  DispatchMountEvent(chromeos::disks::DiskMountManager::UNMOUNTING,
-                     chromeos::MOUNT_ERROR_NONE,
-                     {base::StrCat({"drivefs://", token}),
-                      "/media/drivefsroot/salt-g-ID",
-                      chromeos::MOUNT_TYPE_NETWORK_STORAGE,
-                      {}});
-  EXPECT_FALSE(host_->IsMounted());
-  host_->Unmount();
-}
-
-TEST_F(DriveFsHostTest, MountError) {
-  auto token = StartMount();
-  EXPECT_CALL(*disk_manager_, UnmountPath(_, _, _)).Times(0);
-  EXPECT_CALL(*host_delegate_, OnMountFailed(MountFailure::kInvocation, _));
-
-  DispatchMountEvent(chromeos::disks::DiskMountManager::MOUNTING,
-                     chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED,
-                     {base::StrCat({"drivefs://", token}),
-                      "/media/drivefsroot/g-ID",
-                      chromeos::MOUNT_TYPE_NETWORK_STORAGE,
-                      {}});
-  EXPECT_FALSE(host_->IsMounted());
-  EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
-}
-
 TEST_F(DriveFsHostTest, MountWhileAlreadyMounted) {
   DoMount();
   EXPECT_FALSE(host_->Mount());
 }
 
-TEST_F(DriveFsHostTest, UnmountByRemote) {
-  ASSERT_NO_FATAL_FAILURE(DoMount());
-  base::Optional<base::TimeDelta> delay = base::TimeDelta::FromSeconds(5);
-  EXPECT_CALL(*host_delegate_, OnUnmounted(delay));
-  SendOnUnmounted(delay);
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(DriveFsHostTest, BreakConnectionAfterMount) {
-  ASSERT_NO_FATAL_FAILURE(DoMount());
-  base::Optional<base::TimeDelta> empty;
-  EXPECT_CALL(*host_delegate_, OnUnmounted(empty));
-  delegate_ptr_.reset();
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(DriveFsHostTest, BreakConnectionBeforeMount) {
-  ASSERT_NO_FATAL_FAILURE(EstablishConnection());
-  EXPECT_FALSE(host_->IsMounted());
-
-  base::Optional<base::TimeDelta> empty;
-  EXPECT_CALL(*host_delegate_,
-              OnMountFailed(MountFailure::kIpcDisconnect, empty));
-  delegate_ptr_.reset();
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(DriveFsHostTest, MountTimeout) {
-  auto token = StartMount();
-  DispatchMountSuccessEvent(token);
-  EXPECT_FALSE(host_->IsMounted());
-
-  base::Optional<base::TimeDelta> empty;
-  EXPECT_CALL(*host_delegate_, OnMountFailed(MountFailure::kTimeout, empty));
-  timer_->Fire();
-}
-
-// DiskMountManager sometimes sends mount events for all existing mount points.
-// Mount events beyond the first should be ignored.
-TEST_F(DriveFsHostTest, MultipleMountNotifications) {
-  ASSERT_NO_FATAL_FAILURE(DoMount());
-
-  // That is event is ignored is verified the the expectations set in DoMount().
-  // OnMounted() should only be invoked once.
-  DispatchMountSuccessEvent(token_);
-}
-
 TEST_F(DriveFsHostTest, UnsupportedAccountTypes) {
   EXPECT_CALL(*disk_manager_, MountPath(_, _, _, _, _, _)).Times(0);
   const AccountId unsupported_accounts[] = {
diff --git a/chromeos/network/network_state.cc b/chromeos/network/network_state.cc
index 1e5a1708..8e71e933 100644
--- a/chromeos/network/network_state.cc
+++ b/chromeos/network/network_state.cc
@@ -540,8 +540,10 @@
 
 // static
 bool NetworkState::ErrorIsValid(const std::string& error) {
-  // Shill uses "Unknown" to indicate an unset or cleared error state.
-  return !error.empty() && error != kErrorUnknown;
+  // Pre M-74 Shill uses "Unknown" to indicate an unset or cleared error state.
+  // TODO(stevenjb): Remove kErrorUnknown once 74 has shipped.
+  return !error.empty() && error != kErrorUnknown &&
+         error != shill::kErrorNoFailure;
 }
 
 // static
diff --git a/components/assist_ranker/base_predictor_unittest.cc b/components/assist_ranker/base_predictor_unittest.cc
index 6b330ba..9339229 100644
--- a/components/assist_ranker/base_predictor_unittest.cc
+++ b/components/assist_ranker/base_predictor_unittest.cc
@@ -71,7 +71,7 @@
   // |predictor_config|.
   static std::unique_ptr<FakePredictor> Create(
       PredictorConfig predictor_config);
-  ~FakePredictor() override{};
+  ~FakePredictor() override {}
   // Validation will always succeed.
   static RankerModelStatus ValidateModel(const RankerModel& model) {
     return RankerModelStatus::OK;
@@ -79,7 +79,7 @@
 
  protected:
   // Not implementing any inference logic.
-  bool Initialize() override { return true; };
+  bool Initialize() override { return true; }
 
  private:
   FakePredictor(const PredictorConfig& config) : BasePredictor(config) {}
diff --git a/components/assist_ranker/binary_classifier_predictor.cc b/components/assist_ranker/binary_classifier_predictor.cc
index 402aa59..54e1eb98 100644
--- a/components/assist_ranker/binary_classifier_predictor.cc
+++ b/components/assist_ranker/binary_classifier_predictor.cc
@@ -19,8 +19,8 @@
 
 BinaryClassifierPredictor::BinaryClassifierPredictor(
     const PredictorConfig& config)
-    : BasePredictor(config){};
-BinaryClassifierPredictor::~BinaryClassifierPredictor(){};
+    : BasePredictor(config) {}
+BinaryClassifierPredictor::~BinaryClassifierPredictor() {}
 
 // static
 std::unique_ptr<BinaryClassifierPredictor> BinaryClassifierPredictor::Create(
diff --git a/components/assist_ranker/classifier_predictor.cc b/components/assist_ranker/classifier_predictor.cc
index 31e90bee..80fa065 100644
--- a/components/assist_ranker/classifier_predictor.cc
+++ b/components/assist_ranker/classifier_predictor.cc
@@ -21,8 +21,8 @@
 namespace assist_ranker {
 
 ClassifierPredictor::ClassifierPredictor(const PredictorConfig& config)
-    : BasePredictor(config){};
-ClassifierPredictor::~ClassifierPredictor(){};
+    : BasePredictor(config) {}
+ClassifierPredictor::~ClassifierPredictor() {}
 
 // static
 std::unique_ptr<ClassifierPredictor> ClassifierPredictor::Create(
diff --git a/components/browser_sync/browser_sync_switches.cc b/components/browser_sync/browser_sync_switches.cc
index e970f0a0..39d8763b 100644
--- a/components/browser_sync/browser_sync_switches.cc
+++ b/components/browser_sync/browser_sync_switches.cc
@@ -4,6 +4,8 @@
 
 #include "components/browser_sync/browser_sync_switches.h"
 
+#include "base/command_line.h"
+
 namespace switches {
 
 // Disables syncing browser data to a Google Account.
@@ -24,4 +26,9 @@
 // flag is present.
 const char kLocalSyncBackendDir[] = "local-sync-backend-dir";
 
+bool IsSyncAllowedByFlag() {
+  return !base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kDisableSync);
+}
+
 }  // namespace switches
diff --git a/components/browser_sync/browser_sync_switches.h b/components/browser_sync/browser_sync_switches.h
index fa70667..de0323c 100644
--- a/components/browser_sync/browser_sync_switches.h
+++ b/components/browser_sync/browser_sync_switches.h
@@ -14,6 +14,12 @@
 extern const char kEnableLocalSyncBackend[];
 extern const char kLocalSyncBackendDir[];
 
+// Returns whether sync is allowed to run based on command-line switches.
+// Profile::IsSyncAllowed() is probably a better signal than this function.
+// This function can be called from any thread, and the implementation doesn't
+// assume it's running on the UI thread.
+bool IsSyncAllowedByFlag();
+
 }  // namespace switches
 
 #endif  // COMPONENTS_BROWSER_SYNC_BROWSER_SYNC_SWITCHES_H_
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index dbd27d7..259b581 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -201,7 +201,7 @@
 
   // If Sync is disabled via command line flag, then ProfileSyncService
   // shouldn't be instantiated.
-  DCHECK(IsSyncAllowedByFlag());
+  DCHECK(switches::IsSyncAllowedByFlag());
 
   std::string last_version = sync_prefs_.GetLastRunVersion();
   std::string current_version = PRODUCT_VERSION;
@@ -706,7 +706,7 @@
 
   // If Sync is disabled via command line flag, then ProfileSyncService
   // shouldn't even be instantiated.
-  DCHECK(IsSyncAllowedByFlag());
+  DCHECK(switches::IsSyncAllowedByFlag());
 
   int result = DISABLE_REASON_NONE;
   if (!user_settings_->IsSyncAllowedByPlatform()) {
@@ -1885,12 +1885,6 @@
   return sync_js_controller_.AsWeakPtr();
 }
 
-// static
-bool ProfileSyncService::IsSyncAllowedByFlag() {
-  return !base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableSync);
-}
-
 void ProfileSyncService::StopAndClear() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index ad19786..6c680db7 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -287,12 +287,6 @@
     return crypto_.passphrase_required_reason();
   }
 
-  // Returns whether sync is allowed to run based on command-line switches.
-  // Profile::IsSyncAllowed() is probably a better signal than this function.
-  // This function can be called from any thread, and the implementation doesn't
-  // assume it's running on the UI thread.
-  static bool IsSyncAllowedByFlag();
-
   // syncer::UnrecoverableErrorHandler implementation.
   void OnUnrecoverableError(const base::Location& from_here,
                             const std::string& message) override;
diff --git a/components/browser_sync/profile_sync_service_unittest.cc b/components/browser_sync/profile_sync_service_unittest.cc
index 525d07b7..7c54c0d97 100644
--- a/components/browser_sync/profile_sync_service_unittest.cc
+++ b/components/browser_sync/profile_sync_service_unittest.cc
@@ -1111,12 +1111,12 @@
 // Verify that the disable sync flag disables sync.
 TEST_F(ProfileSyncServiceTest, DisableSyncFlag) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kDisableSync);
-  EXPECT_FALSE(ProfileSyncService::IsSyncAllowedByFlag());
+  EXPECT_FALSE(switches::IsSyncAllowedByFlag());
 }
 
 // Verify that no disable sync flag enables sync.
 TEST_F(ProfileSyncServiceTest, NoDisableSyncFlag) {
-  EXPECT_TRUE(ProfileSyncService::IsSyncAllowedByFlag());
+  EXPECT_TRUE(switches::IsSyncAllowedByFlag());
 }
 
 // Test Sync will stop after receive memory pressure
diff --git a/components/exo/wayland/zcr_keyboard_configuration.cc b/components/exo/wayland/zcr_keyboard_configuration.cc
index 1aee392..d986c54 100644
--- a/components/exo/wayland/zcr_keyboard_configuration.cc
+++ b/components/exo/wayland/zcr_keyboard_configuration.cc
@@ -24,7 +24,7 @@
 // Send a keyboard layout name instead of XKB contents.
 // TODO(tetsui): Remove when the change becomes default.
 const base::Feature kSendKeyboardLayoutNameFeature{
-    "ExoSendKeyboardLayoutName", base::FEATURE_DISABLED_BY_DEFAULT};
+    "ExoSendKeyboardLayoutName", base::FEATURE_ENABLED_BY_DEFAULT};
 
 ////////////////////////////////////////////////////////////////////////////////
 // keyboard_device_configuration interface:
diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc
index 4bed940cf..af53aae 100644
--- a/components/plugins/renderer/webview_plugin.cc
+++ b/components/plugins/renderer/webview_plugin.cc
@@ -69,7 +69,10 @@
   WebViewPlugin* plugin = new WebViewPlugin(render_view, delegate, preferences);
   // Loading may synchronously access |delegate| which could be
   // uninitialized just yet, so load in another task.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      plugin->web_view_helper_.main_frame()->GetTaskRunner(
+          blink::TaskType::kInternalDefault);
+  task_runner->PostTask(
       FROM_HERE,
       base::BindOnce(&WebViewPlugin::LoadHTML,
                      plugin->weak_factory_.GetWeakPtr(), html_data, url));
diff --git a/components/signin/core/browser/account_investigator_unittest.cc b/components/signin/core/browser/account_investigator_unittest.cc
index 15c5d5c..e6f5b19 100644
--- a/components/signin/core/browser/account_investigator_unittest.cc
+++ b/components/signin/core/browser/account_investigator_unittest.cc
@@ -267,7 +267,8 @@
       /*accounts_are_fresh=*/true, just_one, no_accounts};
   GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE);
   investigator()->OnAccountsInCookieUpdated(accounts_in_cookie_jar_info, error);
-  EXPECT_EQ(0u, histogram_tester.GetTotalCountsForPrefix("Signin.").size());
+  EXPECT_EQ(
+      0u, histogram_tester.GetTotalCountsForPrefix("Signin.CookieJar.").size());
 }
 
 TEST_F(AccountInvestigatorTest, OnGaiaAccountsInCookieUpdatedOnChange) {
@@ -294,7 +295,8 @@
       /*accounts_are_fresh=*/true, just_one, no_accounts};
   investigator()->OnAccountsInCookieUpdated(
       accounts_in_cookie_jar_info, GoogleServiceAuthError::AuthErrorNone());
-  EXPECT_EQ(1u, histogram_tester.GetTotalCountsForPrefix("Signin.").size());
+  EXPECT_EQ(
+      1u, histogram_tester.GetTotalCountsForPrefix("Signin.CookieJar.").size());
   ExpectRelationReport(ReportingType::ON_CHANGE, histogram_tester,
                        AccountRelation::SINGLE_SIGNED_IN_MATCH_NO_SIGNED_OUT);
 }
@@ -348,7 +350,8 @@
   const HistogramTester histogram_tester;
   TryPeriodicReport();
   EXPECT_TRUE(*periodic_pending());
-  EXPECT_EQ(0u, histogram_tester.GetTotalCountsForPrefix("Signin.").size());
+  EXPECT_EQ(
+      0u, histogram_tester.GetTotalCountsForPrefix("Signin.CookieJar.").size());
 
   std::string email("f@bar.com");
   identity_test_env()->SetCookieAccounts(
diff --git a/components/signin/core/browser/fake_signin_manager.cc b/components/signin/core/browser/fake_signin_manager.cc
index 50a187a3..953ff28 100644
--- a/components/signin/core/browser/fake_signin_manager.cc
+++ b/components/signin/core/browser/fake_signin_manager.cc
@@ -52,33 +52,12 @@
 
 FakeSigninManager::~FakeSigninManager() {}
 
-void FakeSigninManager::StartSignInWithRefreshToken(
-    const std::string& refresh_token,
-    const std::string& gaia_id,
-    const std::string& username,
-    OAuthTokenFetchedCallback oauth_fetched_callback) {
-  set_auth_in_progress(
-      account_tracker_service()->SeedAccountInfo(gaia_id, username));
-  username_ = username;
-
-  possibly_invalid_gaia_id_.assign(gaia_id);
-  possibly_invalid_email_.assign(username);
-
-  if (!oauth_fetched_callback.is_null())
-    std::move(oauth_fetched_callback).Run(refresh_token);
-}
-
-void FakeSigninManager::CompletePendingSignin() {
-  SetAuthenticatedAccountId(GetAccountIdForAuthInProgress());
-  set_auth_in_progress(std::string());
-  FireGoogleSigninSucceeded();
-}
-
 void FakeSigninManager::SignIn(const std::string& gaia_id,
                                const std::string& username) {
-  StartSignInWithRefreshToken(std::string(), gaia_id, username,
-                              OAuthTokenFetchedCallback());
-  CompletePendingSignin();
+  std::string account_id =
+      account_tracker_service()->SeedAccountInfo(gaia_id, username);
+  token_service()->UpdateCredentials(account_id, "test_refresh_token");
+  OnExternalSigninCompleted(username);
 }
 
 void FakeSigninManager::ForceSignOut() {
@@ -99,18 +78,6 @@
     RemoveAccountsOption remove_option,
     SigninClient::SignoutDecision signout_decision) {
   if (!IsAuthenticated()) {
-    if (AuthInProgress()) {
-      // If the user is in the process of signing in, then treat a call to
-      // SignOut as a cancellation request.
-      GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
-      HandleAuthError(error);
-    } else {
-      // Clean up our transient data and exit if we aren't signed in.
-      // This avoids a perf regression from clearing out the TokenDB if
-      // SignOut() is invoked on startup to clean up any incomplete previous
-      // signin attempts.
-      ClearTransientSigninData();
-    }
     return;
   }
 
@@ -119,7 +86,6 @@
   if (signout_decision == SigninClient::SignoutDecision::DISALLOW_SIGNOUT)
     return;
 
-  set_auth_in_progress(std::string());
   AccountInfo account_info = GetAuthenticatedAccountInfo();
   const std::string account_id = GetAuthenticatedAccountId();
   const std::string username = account_info.email;
diff --git a/components/signin/core/browser/fake_signin_manager.h b/components/signin/core/browser/fake_signin_manager.h
index 0c5239e..a04d04e 100644
--- a/components/signin/core/browser/fake_signin_manager.h
+++ b/components/signin/core/browser/fake_signin_manager.h
@@ -44,24 +44,12 @@
 
   ~FakeSigninManager() override;
 
-  void set_auth_in_progress(const std::string& account_id) {
-    possibly_invalid_account_id_ = account_id;
-  }
-
   void SignIn(const std::string& gaia_id, const std::string& username);
 
   void ForceSignOut();
 
   void FailSignin(const GoogleServiceAuthError& error);
 
-  void StartSignInWithRefreshToken(
-      const std::string& refresh_token,
-      const std::string& gaia_id,
-      const std::string& username,
-      OAuthTokenFetchedCallback oauth_fetched_callback) override;
-
-  void CompletePendingSignin() override;
-
  protected:
   void OnSignoutDecisionReached(
       signin_metrics::ProfileSignout signout_source_metric,
diff --git a/components/signin/core/browser/signin_client.h b/components/signin/core/browser/signin_client.h
index 202cfda..0b288a6 100644
--- a/components/signin/core/browser/signin_client.h
+++ b/components/signin/core/browser/signin_client.h
@@ -95,9 +95,6 @@
       gaia::GaiaSource source,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) = 0;
 
-  // Called once the credentials has been copied to another SigninManager.
-  virtual void AfterCredentialsCopied() {}
-
   // Schedules migration to happen at next startup.
   virtual void SetReadyForDiceMigration(bool is_ready) {}
 };
diff --git a/components/signin/core/browser/signin_manager.cc b/components/signin/core/browser/signin_manager.cc
index e0cfbbf..4250086 100644
--- a/components/signin/core/browser/signin_manager.cc
+++ b/components/signin/core/browser/signin_manager.cc
@@ -32,107 +32,13 @@
     GaiaCookieManagerService* cookie_manager_service,
     signin::AccountConsistencyMethod account_consistency)
     : SigninManagerBase(client, token_service, account_tracker_service),
-      type_(SIGNIN_TYPE_NONE),
       cookie_manager_service_(cookie_manager_service),
       account_consistency_(account_consistency),
-      signin_manager_signed_in_(false),
-      user_info_fetched_by_account_tracker_(false),
       weak_pointer_factory_(this) {}
 
 SigninManager::~SigninManager() {}
 
-std::string SigninManager::SigninTypeToString(SigninManager::SigninType type) {
-  switch (type) {
-    case SIGNIN_TYPE_NONE:
-      return "No Signin";
-    case SIGNIN_TYPE_WITH_REFRESH_TOKEN:
-      return "With refresh token";
-    case SIGNIN_TYPE_WITHOUT_REFRESH_TOKEN:
-      return "Without refresh token";
-  }
-
-  NOTREACHED();
-  return std::string();
-}
-
-bool SigninManager::PrepareForSignin(SigninType type,
-                                     const std::string& gaia_id,
-                                     const std::string& username) {
-  std::string account_id =
-      account_tracker_service()->PickAccountIdForAccount(gaia_id, username);
-  DCHECK(!account_id.empty());
-  DCHECK(possibly_invalid_account_id_.empty() ||
-         possibly_invalid_account_id_ == account_id);
-
-  if (!IsAllowedUsername(username)) {
-    // Account is not allowed by admin policy.
-    HandleAuthError(
-        GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED));
-    return false;
-  }
-
-  // This attempt is either 1) the user trying to establish initial sync, or
-  // 2) trying to refresh credentials for an existing username.  If it is 2, we
-  // need to try again, but take care to leave state around tracking that the
-  // user has successfully signed in once before with this username, so that on
-  // restart we don't think sync setup has never completed.
-  ClearTransientSigninData();
-  type_ = type;
-  possibly_invalid_account_id_.assign(account_id);
-  possibly_invalid_gaia_id_.assign(gaia_id);
-  possibly_invalid_email_.assign(username);
-  signin_manager_signed_in_ = false;
-  user_info_fetched_by_account_tracker_ = false;
-  return true;
-}
-
-void SigninManager::StartSignInWithRefreshToken(
-    const std::string& refresh_token,
-    const std::string& gaia_id,
-    const std::string& username,
-    OAuthTokenFetchedCallback callback) {
-  DCHECK(!IsAuthenticated());
-  SigninType signin_type = refresh_token.empty()
-                               ? SIGNIN_TYPE_WITHOUT_REFRESH_TOKEN
-                               : SIGNIN_TYPE_WITH_REFRESH_TOKEN;
-  if (!PrepareForSignin(signin_type, gaia_id, username)) {
-    return;
-  }
-
-  // Store the refresh token.
-  temp_refresh_token_ = refresh_token;
-
-  if (!callback.is_null()) {
-    // Callback present, let the caller complete the pending sign-in.
-    std::move(callback).Run(temp_refresh_token_);
-  } else {
-    // No callback, so just complete the pending signin.
-    CompletePendingSignin();
-  }
-}
-
-void SigninManager::CopyCredentialsFrom(const SigninManager& source) {
-  DCHECK_NE(this, &source);
-  possibly_invalid_account_id_ = source.possibly_invalid_account_id_;
-  possibly_invalid_gaia_id_ = source.possibly_invalid_gaia_id_;
-  possibly_invalid_email_ = source.possibly_invalid_email_;
-  temp_refresh_token_ = source.temp_refresh_token_;
-  source.signin_client()->AfterCredentialsCopied();
-}
-
-void SigninManager::ClearTransientSigninData() {
-  DCHECK(IsInitialized());
-
-  possibly_invalid_account_id_.clear();
-  possibly_invalid_gaia_id_.clear();
-  possibly_invalid_email_.clear();
-  type_ = SIGNIN_TYPE_NONE;
-  temp_refresh_token_.clear();
-}
-
 void SigninManager::HandleAuthError(const GoogleServiceAuthError& error) {
-  ClearTransientSigninData();
-
   for (auto& observer : observer_list_)
     observer.GoogleSigninFailed(error);
 }
@@ -181,18 +87,6 @@
 
   signin_metrics::LogSignout(signout_source_metric, signout_delete_metric);
   if (!IsAuthenticated()) {
-    if (AuthInProgress()) {
-      // If the user is in the process of signing in, then treat a call to
-      // SignOut as a cancellation request.
-      GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
-      HandleAuthError(error);
-    } else {
-      // Clean up our transient data and exit if we aren't signed in.
-      // This avoids a perf regression from clearing out the TokenDB if
-      // SignOut() is invoked on startup to clean up any incomplete previous
-      // signin attempts.
-      ClearTransientSigninData();
-    }
     return;
   }
 
@@ -203,8 +97,6 @@
     return;
   }
 
-  ClearTransientSigninData();
-
   AccountInfo account_info = GetAuthenticatedAccountInfo();
   const std::string account_id = GetAuthenticatedAccountId();
   const std::string username = account_info.email;
@@ -289,8 +181,6 @@
                               signin_metrics::SignoutDelete::IGNORE_METRIC);
   }
 
-  account_tracker_service()->AddObserver(this);
-
   // It is important to only load credentials after starting to observe the
   // token service.
   token_service()->AddObserver(this);
@@ -298,7 +188,6 @@
 
 void SigninManager::Shutdown() {
   token_service()->RemoveObserver(this);
-  account_tracker_service()->RemoveObserver(this);
   local_state_pref_registrar_.RemoveAll();
   SigninManagerBase::Shutdown();
 }
@@ -322,7 +211,7 @@
 }
 
 void SigninManager::OnSigninAllowedPrefChanged() {
-  if (!IsSigninAllowed() && (IsAuthenticated() || AuthInProgress()))
+  if (!IsSigninAllowed() && IsAuthenticated())
     SignOut(signin_metrics::SIGNOUT_PREF_CHANGED,
             signin_metrics::SignoutDelete::IGNORE_METRIC);
 }
@@ -343,22 +232,6 @@
   return identity::IsUsernameAllowedByPattern(username, pattern);
 }
 
-bool SigninManager::AuthInProgress() const {
-  return !possibly_invalid_account_id_.empty();
-}
-
-const std::string& SigninManager::GetAccountIdForAuthInProgress() const {
-  return possibly_invalid_account_id_;
-}
-
-const std::string& SigninManager::GetGaiaIdForAuthInProgress() const {
-  return possibly_invalid_gaia_id_;
-}
-
-const std::string& SigninManager::GetUsernameForAuthInProgress() const {
-  return possibly_invalid_email_;
-}
-
 void SigninManager::MergeSigninCredentialIntoCookieJar() {
   if (account_consistency_ == signin::AccountConsistencyMethod::kMirror)
     return;
@@ -370,49 +243,19 @@
                                               gaia::GaiaSource::kSigninManager);
 }
 
-void SigninManager::CompletePendingSignin() {
-  DCHECK(!possibly_invalid_account_id_.empty());
-  OnSignedIn();
-
-  DCHECK(IsAuthenticated());
-
-  if (!temp_refresh_token_.empty()) {
-    std::string account_id = GetAuthenticatedAccountId();
-    token_service()->UpdateCredentials(
-        account_id, temp_refresh_token_,
-        signin_metrics::SourceForRefreshTokenOperation::
-            kSigninManager_LegacyPreDiceSigninFlow);
-    temp_refresh_token_.clear();
-  }
-  MergeSigninCredentialIntoCookieJar();
-}
-
 void SigninManager::OnExternalSigninCompleted(const std::string& username) {
   AccountInfo info =
       account_tracker_service()->FindAccountInfoByEmail(username);
   DCHECK(!info.gaia.empty());
   DCHECK(!info.email.empty());
-  possibly_invalid_account_id_ = info.account_id;
-  possibly_invalid_gaia_id_ = info.gaia;
-  possibly_invalid_email_ = info.email;
-  OnSignedIn();
-}
 
-void SigninManager::OnSignedIn() {
   bool reauth_in_progress = IsAuthenticated();
 
   signin_client()->GetPrefs()->SetInt64(
       prefs::kSignedInTime,
       base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
 
-  SetAuthenticatedAccountInfo(possibly_invalid_gaia_id_,
-                              possibly_invalid_email_);
-  const std::string gaia_id = possibly_invalid_gaia_id_;
-
-  possibly_invalid_account_id_.clear();
-  possibly_invalid_gaia_id_.clear();
-  possibly_invalid_email_.clear();
-  signin_manager_signed_in_ = true;
+  SetAuthenticatedAccountInfo(info.gaia, info.email);
 
   if (!reauth_in_progress)
     FireGoogleSigninSucceeded();
@@ -433,17 +276,6 @@
   }
 }
 
-void SigninManager::OnAccountUpdated(const AccountInfo& info) {
-  if (!info.IsValid())
-    return;
-
-  user_info_fetched_by_account_tracker_ = true;
-}
-
-void SigninManager::OnAccountUpdateFailed(const std::string& account_id) {
-  user_info_fetched_by_account_tracker_ = true;
-}
-
 void SigninManager::OnRefreshTokensLoaded() {
   token_service()->RemoveObserver(this);
 
diff --git a/components/signin/core/browser/signin_manager.h b/components/signin/core/browser/signin_manager.h
index 0141fef..2efe1c19 100644
--- a/components/signin/core/browser/signin_manager.h
+++ b/components/signin/core/browser/signin_manager.h
@@ -48,16 +48,8 @@
 }  // namespace identity
 
 class SigninManager : public SigninManagerBase,
-                      public AccountTrackerService::Observer,
                       public OAuth2TokenService::Observer {
  public:
-  // The callback invoked once the OAuth token has been fetched during signin,
-  // but before the profile transitions to the "signed-in" state. This allows
-  // callers to load policy and prompt the user appropriately before completing
-  // signin. The callback is passed the just-fetched OAuth login refresh token.
-  using OAuthTokenFetchedCallback =
-      base::OnceCallback<void(const std::string&)>;
-
   // Used to remove accounts from the token service and the account tracker.
   enum class RemoveAccountsOption {
     // Do not remove accounts.
@@ -87,24 +79,6 @@
   // are actually SigninManager instances.
   static SigninManager* FromSigninManagerBase(SigninManagerBase* manager);
 
-  // Attempt to sign in this user with a refresh token.
-  // If |refresh_token| is not empty, then SigninManager will add it to the
-  // |token_service_| when the sign-in flow is completed.
-  // If non-null, the passed |oauth_fetched_callback| callback is invoked once
-  // sign-in has been completed.
-  // The callback should invoke SignOut() or CompletePendingSignin() to either
-  // continue or cancel the in-process signin.
-  virtual void StartSignInWithRefreshToken(
-      const std::string& refresh_token,
-      const std::string& gaia_id,
-      const std::string& username,
-      OAuthTokenFetchedCallback oauth_fetched_callback);
-
-  // Copies auth credentials from one SigninManager to this one. This is used
-  // when creating a new profile during the signin process to transfer the
-  // in-progress credentials to the new profile.
-  virtual void CopyCredentialsFrom(const SigninManager& source);
-
   // Signs a user out, removing the preference, erasing all keys
   // associated with the authenticated user, and canceling all auth in progress.
   // On mobile and on desktop pre-DICE, this also removes all accounts from
@@ -140,18 +114,12 @@
   // If applicable, merge the signed in account into the cookie jar.
   void MergeSigninCredentialIntoCookieJar();
 
-  // Invoked from an OAuthTokenFetchedCallback to complete user signin.
-  virtual void CompletePendingSignin();
-
   // Invoked from SigninManagerAndroid to indicate that the sign-in process
   // has completed for the email |username|.  SigninManager assumes that
   // |username| can be used to look up the corresponding account_id and gaia_id
   // for this email.
   void OnExternalSigninCompleted(const std::string& username);
 
-  // Returns true if there's a signin in progress.
-  bool AuthInProgress() const override;
-
   // Returns whether sign-in is allowed.
   // TODO(crbug.com/806778): Remove method in super-class.
   bool IsSigninAllowed() const override;
@@ -159,18 +127,6 @@
   // Sets whether sign-in is allowed or not.
   void SetSigninAllowed(bool allowed);
 
-  // If an authentication is in progress, return the account id being
-  // authenticated. Returns an empty string if no auth is in progress.
-  const std::string& GetAccountIdForAuthInProgress() const;
-
-  // If an authentication is in progress, return the gaia id being
-  // authenticated. Returns an empty string if no auth is in progress.
-  const std::string& GetGaiaIdForAuthInProgress() const;
-
-  // If an authentication is in progress, return the username being
-  // authenticated. Returns an empty string if no auth is in progress.
-  const std::string& GetUsernameForAuthInProgress() const;
-
  protected:
   // The sign out process which is started by SigninClient::PreSignOut()
   virtual void OnSignoutDecisionReached(
@@ -180,50 +136,20 @@
       SigninClient::SignoutDecision signout_decision);
 
  private:
-  enum SigninType {
-    SIGNIN_TYPE_NONE,
-    SIGNIN_TYPE_WITH_REFRESH_TOKEN,
-    SIGNIN_TYPE_WITHOUT_REFRESH_TOKEN
-  };
-
-  std::string SigninTypeToString(SigninType type);
   friend class FakeSigninManager;
   friend class identity::IdentityManager;
   FRIEND_TEST_ALL_PREFIXES(SigninManagerTest, Prohibited);
   FRIEND_TEST_ALL_PREFIXES(SigninManagerTest, TestAlternateWildcard);
 
-  // Called to setup the transient signin data during one of the
-  // StartSigninXXX methods.  |type| indicates which of the methods is being
-  // used to perform the signin while |username| identifies the account to be
-  // signed in. Returns false and generates an auth error if the passed
-  // |username| is not allowed by policy.  |gaia_id| is the obfuscated gaia id
-  // corresponding to |username|.
-  bool PrepareForSignin(SigninType type,
-                        const std::string& gaia_id,
-                        const std::string& username);
-
-  // Persists |account_id| as the currently signed-in account, and triggers
-  // a sign-in success notification.
-  void OnSignedIn();
-
   // Send all observers |GoogleSigninSucceeded| notifications.
   void FireGoogleSigninSucceeded();
 
   // Send all observers |GoogleSignedOut| notifications.
   void FireGoogleSignedOut(const AccountInfo& account_info);
 
-  // AccountTrackerService::Observer:
-  void OnAccountUpdated(const AccountInfo& info) override;
-  void OnAccountUpdateFailed(const std::string& account_id) override;
-
   // OAuth2TokenService::Observer:
   void OnRefreshTokensLoaded() override;
 
-  // Called when a new request to re-authenticate a user is in progress.
-  // Will clear in memory data but leaves the db as such so when the browser
-  // restarts we can use the old token(which might throw a password error).
-  void ClearTransientSigninData();
-
   // Called to handle an error from a GAIA auth fetch.  Sets the last error
   // to |error|, sends out a notification of login failure and clears the
   // transient signin data.
@@ -240,19 +166,6 @@
   // Returns true if the passed username is allowed by policy.
   bool IsAllowedUsername(const std::string& username) const;
 
-  std::string possibly_invalid_account_id_;
-  std::string possibly_invalid_gaia_id_;
-  std::string possibly_invalid_email_;
-
-  // The type of sign being performed.  This value is valid only between a call
-  // to one of the StartSigninXXX methods and when the sign in is either
-  // successful or not.
-  SigninType type_;
-
-  // Temporarily saves the oauth2 refresh token.  It will be passed to the
-  // token service so that it does not need to mint new ones.
-  std::string temp_refresh_token_;
-
   // Object used to use the token to push a GAIA cookie into the cookie jar.
   GaiaCookieManagerService* cookie_manager_service_;
 
@@ -265,12 +178,6 @@
 
   signin::AccountConsistencyMethod account_consistency_;
 
-  // Two gate conditions for when PostSignedIn should be called. Verify
-  // that the SigninManager has reached OnSignedIn() and the AccountTracker
-  // has completed calling GetUserInfo.
-  bool signin_manager_signed_in_;
-  bool user_info_fetched_by_account_tracker_;
-
   base::WeakPtrFactory<SigninManager> weak_pointer_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SigninManager);
diff --git a/components/signin/core/browser/signin_manager_base.cc b/components/signin/core/browser/signin_manager_base.cc
index 4eaecf6..1e22abb 100644
--- a/components/signin/core/browser/signin_manager_base.cc
+++ b/components/signin/core/browser/signin_manager_base.cc
@@ -229,11 +229,6 @@
   return !authenticated_account_id_.empty();
 }
 
-bool SigninManagerBase::AuthInProgress() const {
-  // SigninManagerBase never kicks off auth processes itself.
-  return false;
-}
-
 void SigninManagerBase::Shutdown() {
   on_shutdown_callback_list_.Notify();
 }
diff --git a/components/signin/core/browser/signin_manager_base.h b/components/signin/core/browser/signin_manager_base.h
index 5a80e91..f618edd 100644
--- a/components/signin/core/browser/signin_manager_base.h
+++ b/components/signin/core/browser/signin_manager_base.h
@@ -126,9 +126,6 @@
   // Returns true if there is an authenticated user.
   bool IsAuthenticated() const;
 
-  // Returns true if there's a signin in progress.
-  virtual bool AuthInProgress() const;
-
   // KeyedService implementation.
   void Shutdown() override;
 
diff --git a/components/signin/core/browser/signin_manager_unittest.cc b/components/signin/core/browser/signin_manager_unittest.cc
index 8d5711d..517a5738 100644
--- a/components/signin/core/browser/signin_manager_unittest.cc
+++ b/components/signin/core/browser/signin_manager_unittest.cc
@@ -141,11 +141,6 @@
     EXPECT_EQ(0, test_observer_.num_failed_signins_);
   }
 
-  void CompleteSigninCallback(const std::string& oauth_token) {
-    oauth_tokens_fetched_.push_back(oauth_token);
-    manager_->CompletePendingSignin();
-  }
-
   base::test::ScopedTaskEnvironment task_environment_;
   sync_preferences::TestingPrefServiceSyncable user_prefs_;
   TestingPrefServiceSimple local_state_;
@@ -161,63 +156,11 @@
   signin::AccountConsistencyMethod account_consistency_;
 };
 
-TEST_F(SigninManagerTest, SignInWithRefreshToken) {
-  CreateSigninManager();
-  EXPECT_FALSE(manager_->IsAuthenticated());
-
-  std::string account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
-  manager_->StartSignInWithRefreshToken(
-      "rt", "gaia_id", "user@gmail.com",
-      SigninManager::OAuthTokenFetchedCallback());
-
-  ExpectSignInWithRefreshTokenSuccess();
-
-  // Should persist across resets.
-  ShutDownManager();
-  CreateSigninManager();
-  EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
-}
-
-TEST_F(SigninManagerTest, SignInWithRefreshTokenCallbackComplete) {
-  CreateSigninManager();
-  EXPECT_FALSE(manager_->IsAuthenticated());
-
-  manager_->StartSignInWithRefreshToken(
-      "rt", "gaia_id", "user@gmail.com",
-      base::BindOnce(&SigninManagerTest::CompleteSigninCallback,
-                     base::Unretained(this)));
-
-  ExpectSignInWithRefreshTokenSuccess();
-  ASSERT_EQ(1U, oauth_tokens_fetched_.size());
-  EXPECT_EQ(oauth_tokens_fetched_[0], "rt");
-}
-
-TEST_F(SigninManagerTest, SignInWithRefreshTokenCallsPostSignout) {
-  CreateSigninManager();
-  EXPECT_FALSE(manager_->IsAuthenticated());
-
-  std::string gaia_id = "12345";
-  std::string email = "user@google.com";
-
-  account_tracker()->SeedAccountInfo(gaia_id, email);
-  account_fetcher()->OnRefreshTokensLoaded();
-
-  manager_->StartSignInWithRefreshToken(
-      "rt1", gaia_id, email, SigninManager::OAuthTokenFetchedCallback());
-
-  account_fetcher()->FakeUserInfoFetchSuccess(
-      account_tracker()->PickAccountIdForAccount(gaia_id, email), email,
-      gaia_id, "google.com", "full_name", "given_name", "locale",
-      "http://www.google.com");
-
-  ExpectSignInWithRefreshTokenSuccess();
-}
-
 TEST_F(SigninManagerTest, SignOut) {
   CreateSigninManager();
-  manager_->StartSignInWithRefreshToken(
-      "rt", "gaia_id", "user@gmail.com",
-      SigninManager::OAuthTokenFetchedCallback());
+  std::string main_account_id =
+      AddToAccountTracker("account_id", "user@gmail.com");
+  manager_->OnExternalSigninCompleted("user@gmail.com");
   manager_->SignOut(signin_metrics::SIGNOUT_TEST,
                     signin_metrics::SignoutDelete::IGNORE_METRIC);
   EXPECT_FALSE(manager_->IsAuthenticated());
diff --git a/components/viz/client/client_resource_provider.cc b/components/viz/client/client_resource_provider.cc
index 7110be6..efca8bb 100644
--- a/components/viz/client/client_resource_provider.cc
+++ b/components/viz/client/client_resource_provider.cc
@@ -10,6 +10,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/gpu/raster_context_provider.h"
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "components/viz/common/resources/resource_sizes.h"
 #include "components/viz/common/resources/returned_resource.h"
@@ -94,6 +95,35 @@
     const std::vector<ResourceId>& export_ids,
     std::vector<TransferableResource>* list,
     ContextProvider* context_provider) {
+  auto cb = base::BindOnce(
+      [](scoped_refptr<ContextProvider> context_provider,
+         std::vector<GLbyte*>* tokens) {
+        context_provider->ContextGL()->VerifySyncTokensCHROMIUM(tokens->data(),
+                                                                tokens->size());
+      },
+      base::WrapRefCounted(context_provider));
+  PrepareSendToParentInternal(export_ids, list, std::move(cb));
+}
+
+void ClientResourceProvider::PrepareSendToParent(
+    const std::vector<ResourceId>& export_ids,
+    std::vector<TransferableResource>* list,
+    RasterContextProvider* context_provider) {
+  PrepareSendToParentInternal(
+      export_ids, list,
+      base::BindOnce(
+          [](scoped_refptr<RasterContextProvider> context_provider,
+             std::vector<GLbyte*>* tokens) {
+            context_provider->RasterInterface()->VerifySyncTokensCHROMIUM(
+                tokens->data(), tokens->size());
+          },
+          base::WrapRefCounted(context_provider)));
+}
+
+void ClientResourceProvider::PrepareSendToParentInternal(
+    const std::vector<ResourceId>& export_ids,
+    std::vector<TransferableResource>* list,
+    base::OnceCallback<void(std::vector<GLbyte*>* tokens)> verify_sync_tokens) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // This function goes through the array multiple times, store the resources
@@ -122,9 +152,8 @@
 
   if (!unverified_sync_tokens.empty()) {
     DCHECK(verified_sync_tokens_required_);
-    DCHECK(context_provider);
-    context_provider->ContextGL()->VerifySyncTokensCHROMIUM(
-        unverified_sync_tokens.data(), unverified_sync_tokens.size());
+    DCHECK(verify_sync_tokens);
+    std::move(verify_sync_tokens).Run(&unverified_sync_tokens);
   }
 
   for (ImportedResource* imported : imports) {
diff --git a/components/viz/client/client_resource_provider.h b/components/viz/client/client_resource_provider.h
index 6de5121..f3965af 100644
--- a/components/viz/client/client_resource_provider.h
+++ b/components/viz/client/client_resource_provider.h
@@ -32,6 +32,7 @@
 
 namespace viz {
 class ContextProvider;
+class RasterContextProvider;
 
 // This class is used to give an integer name (ResourceId) to a gpu or software
 // resource (shipped as a TransferableResource), in order to use that name in
@@ -58,6 +59,13 @@
   void PrepareSendToParent(
       const std::vector<ResourceId>& resource_ids,
       std::vector<TransferableResource>* transferable_resources,
+      RasterContextProvider* context_provider);
+
+  // TODO(sergeyu): Remove after updating all callers to use the above version
+  // of this method.
+  void PrepareSendToParent(
+      const std::vector<ResourceId>& resource_ids,
+      std::vector<TransferableResource>* transferable_resources,
       ContextProvider* context_provider);
 
   // Receives resources from the parent, moving them from mailboxes. ResourceIds
@@ -126,6 +134,12 @@
  private:
   struct ImportedResource;
 
+  void PrepareSendToParentInternal(
+      const std::vector<ResourceId>& export_ids,
+      std::vector<TransferableResource>* list,
+      base::OnceCallback<void(std::vector<GLbyte*>* tokens)>
+          verify_sync_tokens);
+
   THREAD_CHECKER(thread_checker_);
   const bool verified_sync_tokens_required_;
 
diff --git a/components/viz/service/display/display_resource_provider_unittest.cc b/components/viz/service/display/display_resource_provider_unittest.cc
index cdc51d5..52c6bb91 100644
--- a/components/viz/service/display/display_resource_provider_unittest.cc
+++ b/components/viz/service/display/display_resource_provider_unittest.cc
@@ -465,8 +465,9 @@
 
   // Transfer some resources to the parent.
   std::vector<TransferableResource> list;
-  child_resource_provider_->PrepareSendToParent({id1}, &list,
-                                                child_context_provider_.get());
+  child_resource_provider_->PrepareSendToParent(
+      {id1}, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
   ASSERT_EQ(1u, list.size());
   EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
 
@@ -519,7 +520,8 @@
     // Transfer some resources to the parent.
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(
-        {id1}, &list, child_context_provider_.get());
+        {id1}, &list,
+        static_cast<RasterContextProvider*>(child_context_provider_.get()));
     ASSERT_EQ(1u, list.size());
     EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
 
@@ -583,8 +585,9 @@
 
   // Transfer some resources to the parent.
   std::vector<TransferableResource> list;
-  child_resource_provider_->PrepareSendToParent({id1}, &list,
-                                                child_context_provider_.get());
+  child_resource_provider_->PrepareSendToParent(
+      {id1}, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
   ASSERT_EQ(1u, list.size());
   EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
   EXPECT_TRUE(list[0].read_lock_fences_enabled);
@@ -642,8 +645,9 @@
 
   // Transfer resources to the parent.
   std::vector<TransferableResource> list;
-  child_resource_provider_->PrepareSendToParent({id1, id2}, &list,
-                                                child_context_provider_.get());
+  child_resource_provider_->PrepareSendToParent(
+      {id1, id2}, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
   ASSERT_EQ(2u, list.size());
   EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
   EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
@@ -703,8 +707,9 @@
 
   // Transfer resources to the parent.
   std::vector<TransferableResource> list;
-  child_resource_provider_->PrepareSendToParent({id1, id2}, &list,
-                                                child_context_provider_.get());
+  child_resource_provider_->PrepareSendToParent(
+      {id1, id2}, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
   ASSERT_EQ(2u, list.size());
   EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
   EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
@@ -770,7 +775,8 @@
     // Transfer some resources to the parent.
     std::vector<TransferableResource> list;
     no_token_resource_provider->PrepareSendToParent(
-        {id}, &list, child_context_provider_.get());
+        {id}, &list,
+        static_cast<RasterContextProvider*>(child_context_provider_.get()));
     ASSERT_EQ(1u, list.size());
     // A given sync point should be passed through.
     EXPECT_EQ(external_sync_token, list[0].mailbox_holder.sync_token);
@@ -832,8 +838,9 @@
   std::vector<ResourceId> resource_ids_to_transfer(ids, ids + kTotalResources);
 
   std::vector<TransferableResource> list;
-  child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list,
-                                                child_context_provider_.get());
+  child_resource_provider_->PrepareSendToParent(
+      resource_ids_to_transfer, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
   ASSERT_EQ(kTotalResources, list.size());
   for (const auto& id : ids)
     EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id));
@@ -951,8 +958,9 @@
   std::vector<ReturnedResource> returned_to_child;
   int child_id = resource_provider_->CreateChild(
       base::BindRepeating(&CollectResources, &returned_to_child), true);
-  child_resource_provider_->PrepareSendToParent({resource_id}, &send_to_parent,
-                                                child_context_provider_.get());
+  child_resource_provider_->PrepareSendToParent(
+      {resource_id}, &send_to_parent,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
   resource_provider_->ReceiveFromChild(child_id, send_to_parent);
 
   // In DisplayResourceProvider's namespace, use the mapped resource id.
@@ -1070,8 +1078,9 @@
     std::vector<ReturnedResource> returned_to_child;
     int child_id = resource_provider->CreateChild(
         base::BindRepeating(&CollectResources, &returned_to_child), true);
-    child_resource_provider->PrepareSendToParent({resource_id}, &send_to_parent,
-                                                 child_context_provider.get());
+    child_resource_provider->PrepareSendToParent(
+        {resource_id}, &send_to_parent,
+        static_cast<RasterContextProvider*>(child_context_provider.get()));
     resource_provider->ReceiveFromChild(child_id, send_to_parent);
 
     // In DisplayResourceProvider's namespace, use the mapped resource id.
@@ -1223,8 +1232,9 @@
   std::vector<ReturnedResource> returned_to_child;
   int child_id = resource_provider->CreateChild(
       base::BindRepeating(&CollectResources, &returned_to_child), true);
-  child_resource_provider->PrepareSendToParent({resource_id}, &send_to_parent,
-                                               child_context_provider_.get());
+  child_resource_provider->PrepareSendToParent(
+      {resource_id}, &send_to_parent,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
   resource_provider->ReceiveFromChild(child_id, send_to_parent);
 
   // Before create DrawQuad in DisplayResourceProvider's namespace, get the
@@ -1357,8 +1367,9 @@
 
   // Transfer some resources to the parent.
   std::vector<TransferableResource> list;
-  child_resource_provider_->PrepareSendToParent({id1, id2}, &list,
-                                                child_context_provider_.get());
+  child_resource_provider_->PrepareSendToParent(
+      {id1, id2}, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
   ASSERT_EQ(2u, list.size());
   resource_provider_->ReceiveFromChild(child_id, list);
   std::unordered_map<ResourceId, ResourceId> resource_map =
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc
index 2c36d6f..4aae5a92 100644
--- a/components/viz/service/display/gl_renderer_unittest.cc
+++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -2226,8 +2226,9 @@
   std::vector<ResourceId> resource_ids_to_transfer;
   resource_ids_to_transfer.push_back(resource_id);
   std::vector<TransferableResource> list;
-  child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, &list,
-                                               child_context_provider.get());
+  child_resource_provider->PrepareSendToParent(
+      resource_ids_to_transfer, &list,
+      static_cast<RasterContextProvider*>(child_context_provider.get()));
   parent_resource_provider->ReceiveFromChild(child_id, list);
 
   // In DisplayResourceProvider's namespace, use the mapped resource id.
@@ -2438,8 +2439,9 @@
   std::vector<ResourceId> resource_ids_to_transfer;
   resource_ids_to_transfer.push_back(resource_id);
   std::vector<TransferableResource> list;
-  child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, &list,
-                                               child_context_provider.get());
+  child_resource_provider->PrepareSendToParent(
+      resource_ids_to_transfer, &list,
+      static_cast<RasterContextProvider*>(child_context_provider.get()));
   parent_resource_provider->ReceiveFromChild(child_id, list);
 
   // In DisplayResourceProvider's namespace, use the mapped resource id.
@@ -2824,8 +2826,9 @@
   std::vector<ResourceId> resource_ids_to_transfer;
   resource_ids_to_transfer.push_back(resource_id);
   std::vector<TransferableResource> list;
-  child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, &list,
-                                               child_context_provider.get());
+  child_resource_provider->PrepareSendToParent(
+      resource_ids_to_transfer, &list,
+      static_cast<RasterContextProvider*>(child_context_provider.get()));
   parent_resource_provider->ReceiveFromChild(child_id, list);
   // In DisplayResourceProvider's namespace, use the mapped resource id.
   std::unordered_map<ResourceId, ResourceId> resource_map =
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index d5d5546..bedffa1 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -1249,11 +1249,13 @@
     constexpr int kMaxResourceSize = 10000;
 
     video_resource_updater_ = std::make_unique<media::VideoResourceUpdater>(
-        this->child_context_provider_.get(), nullptr,
+        this->child_context_provider_.get(),
+        /*raster_context_provider=*/nullptr, nullptr,
         this->child_resource_provider_.get(), kUseStreamVideoDrawQuad,
         kUseGpuMemoryBufferResources, kUseR16Texture, kMaxResourceSize);
     video_resource_updater2_ = std::make_unique<media::VideoResourceUpdater>(
-        this->child_context_provider_.get(), nullptr,
+        this->child_context_provider_.get(),
+        /*raster_context_provider=*/nullptr, nullptr,
         this->child_resource_provider_.get(), kUseStreamVideoDrawQuad,
         kUseGpuMemoryBufferResources, kUseR16Texture, kMaxResourceSize);
   }
@@ -1652,9 +1654,9 @@
     constexpr bool kUseR16Texture = false;
     constexpr int kMaxResourceSize = 10000;
     video_resource_updater_ = std::make_unique<media::VideoResourceUpdater>(
-        child_context_provider_.get(), nullptr, child_resource_provider_.get(),
-        kUseStreamVideoDrawQuad, kUseGpuMemoryBufferResources, kUseR16Texture,
-        kMaxResourceSize);
+        child_context_provider_.get(), nullptr, nullptr,
+        child_resource_provider_.get(), kUseStreamVideoDrawQuad,
+        kUseGpuMemoryBufferResources, kUseR16Texture, kMaxResourceSize);
   }
 
   void TearDown() override {
diff --git a/components/viz/service/display_embedder/gl_output_surface.cc b/components/viz/service/display_embedder/gl_output_surface.cc
index 35cee0df..49b3cb08 100644
--- a/components/viz/service/display_embedder/gl_output_surface.cc
+++ b/components/viz/service/display_embedder/gl_output_surface.cc
@@ -4,7 +4,8 @@
 
 #include "components/viz/service/display_embedder/gl_output_surface.h"
 
-#include <stdint.h>
+#include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -107,6 +108,10 @@
   if (frame.sub_buffer_rect) {
     HandlePartialSwap(*frame.sub_buffer_rect, flags, std::move(swap_callback),
                       std::move(presentation_callback));
+  } else if (!frame.content_bounds.empty()) {
+    context_provider_->ContextSupport()->SwapWithBounds(
+        frame.content_bounds, flags, std::move(swap_callback),
+        std::move(presentation_callback));
   } else {
     context_provider_->ContextSupport()->Swap(flags, std::move(swap_callback),
                                               std::move(presentation_callback));
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 7f0ae29..99ec733 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
@@ -37,15 +37,13 @@
   // implementation.
   capabilities_.max_frames_pending = 2;
 
-  buffer_queue_.reset(new BufferQueue(
+  buffer_queue_ = std::make_unique<BufferQueue>(
       context_provider->ContextGL(), target, internalformat, buffer_format,
-      gpu_memory_buffer_manager, surface_handle));
+      gpu_memory_buffer_manager, surface_handle);
   buffer_queue_->Initialize();
 }
 
-GLOutputSurfaceBufferQueue::~GLOutputSurfaceBufferQueue() {
-  // TODO(rjkroege): Support cleanup.
-}
+GLOutputSurfaceBufferQueue::~GLOutputSurfaceBufferQueue() = default;
 
 void GLOutputSurfaceBufferQueue::BindFramebuffer() {
   DCHECK(buffer_queue_);
@@ -89,7 +87,6 @@
 }
 
 bool GLOutputSurfaceBufferQueue::IsDisplayedAsOverlayPlane() const {
-  // TODO(rjkroege): implement remaining overlay functionality.
   return true;
 }
 
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 8c42482..6a563a5 100644
--- a/components/viz/service/display_embedder/gl_output_surface_ozone.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_ozone.cc
@@ -23,6 +23,12 @@
                                  internal_format,
                                  display::DisplaySnapshot::PrimaryFormat()) {}
 
-GLOutputSurfaceOzone::~GLOutputSurfaceOzone() {}
+GLOutputSurfaceOzone::~GLOutputSurfaceOzone() = default;
+
+OverlayCandidateValidator* GLOutputSurfaceOzone::GetOverlayCandidateValidator()
+    const {
+  // TODO(crbug.com/930173): Implement overlay functionality.
+  return nullptr;
+}
 
 }  // namespace viz
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 47a7cff..3dc0919 100644
--- a/components/viz/service/display_embedder/gl_output_surface_ozone.h
+++ b/components/viz/service/display_embedder/gl_output_surface_ozone.h
@@ -20,6 +20,9 @@
       uint32_t internal_format);
   ~GLOutputSurfaceOzone() override;
 
+  // OutputSurface implementation.
+  OverlayCandidateValidator* GetOverlayCandidateValidator() const override;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(GLOutputSurfaceOzone);
 };
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h
index 691f2e2..aa3dfcf 100644
--- a/components/viz/service/gl/gpu_service_impl.h
+++ b/components/viz/service/gl/gpu_service_impl.h
@@ -98,6 +98,66 @@
   // accordingly. This can safely be called from any thread.
   void DisableGpuCompositing();
 
+  // mojom::GpuService:
+  void EstablishGpuChannel(int32_t client_id,
+                           uint64_t client_tracing_id,
+                           bool is_gpu_host,
+                           bool cache_shaders_on_disk,
+                           EstablishGpuChannelCallback callback) override;
+  void CloseChannel(int32_t client_id) override;
+#if defined(OS_CHROMEOS)
+  void CreateArcVideoDecodeAccelerator(
+      arc::mojom::VideoDecodeAcceleratorRequest vda_request) override;
+  void CreateArcVideoEncodeAccelerator(
+      arc::mojom::VideoEncodeAcceleratorRequest vea_request) override;
+  void CreateArcVideoProtectedBufferAllocator(
+      arc::mojom::VideoProtectedBufferAllocatorRequest pba_request) override;
+  void CreateArcProtectedBufferManager(
+      arc::mojom::ProtectedBufferManagerRequest pbm_request) override;
+#endif  // defined(OS_CHROMEOS)
+  void CreateJpegDecodeAccelerator(
+      media::mojom::JpegDecodeAcceleratorRequest jda_request) override;
+  void CreateJpegEncodeAccelerator(
+      media::mojom::JpegEncodeAcceleratorRequest jea_request) override;
+  void CreateVideoEncodeAcceleratorProvider(
+      media::mojom::VideoEncodeAcceleratorProviderRequest vea_provider_request)
+      override;
+  void CreateGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+                             const gfx::Size& size,
+                             gfx::BufferFormat format,
+                             gfx::BufferUsage usage,
+                             int client_id,
+                             gpu::SurfaceHandle surface_handle,
+                             CreateGpuMemoryBufferCallback callback) override;
+  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+                              int client_id,
+                              const gpu::SyncToken& sync_token) override;
+  void GetVideoMemoryUsageStats(
+      GetVideoMemoryUsageStatsCallback callback) override;
+#if defined(OS_WIN)
+  void RequestCompleteGpuInfo(RequestCompleteGpuInfoCallback callback) override;
+  void GetGpuSupportedRuntimeVersion(
+      GetGpuSupportedRuntimeVersionCallback callback) override;
+#endif
+  void RequestHDRStatus(RequestHDRStatusCallback callback) override;
+  void LoadedShader(int32_t client_id,
+                    const std::string& key,
+                    const std::string& data) override;
+  void WakeUpGpu() override;
+  void GpuSwitched() override;
+  void DestroyAllChannels() override;
+  void OnBackgroundCleanup() override;
+  void OnBackgrounded() override;
+  void OnForegrounded() override;
+#if defined(OS_MACOSX)
+  void BeginCATransaction() override;
+  void CommitCATransaction(CommitCATransactionCallback callback) override;
+#endif
+  void Crash() override;
+  void Hang() override;
+  void ThrowJavaException() override;
+  void Stop(StopCallback callback) override;
+
   bool is_initialized() const { return !!gpu_host_; }
 
   media::MediaGpuChannelManager* media_gpu_channel_manager() {
@@ -189,66 +249,6 @@
 #endif
   void SetActiveURL(const GURL& url) override;
 
-  // mojom::GpuService:
-  void EstablishGpuChannel(int32_t client_id,
-                           uint64_t client_tracing_id,
-                           bool is_gpu_host,
-                           bool cache_shaders_on_disk,
-                           EstablishGpuChannelCallback callback) override;
-  void CloseChannel(int32_t client_id) override;
-#if defined(OS_CHROMEOS)
-  void CreateArcVideoDecodeAccelerator(
-      arc::mojom::VideoDecodeAcceleratorRequest vda_request) override;
-  void CreateArcVideoEncodeAccelerator(
-      arc::mojom::VideoEncodeAcceleratorRequest vea_request) override;
-  void CreateArcVideoProtectedBufferAllocator(
-      arc::mojom::VideoProtectedBufferAllocatorRequest pba_request) override;
-  void CreateArcProtectedBufferManager(
-      arc::mojom::ProtectedBufferManagerRequest pbm_request) override;
-#endif  // defined(OS_CHROMEOS)
-  void CreateJpegDecodeAccelerator(
-      media::mojom::JpegDecodeAcceleratorRequest jda_request) override;
-  void CreateJpegEncodeAccelerator(
-      media::mojom::JpegEncodeAcceleratorRequest jea_request) override;
-  void CreateVideoEncodeAcceleratorProvider(
-      media::mojom::VideoEncodeAcceleratorProviderRequest vea_provider_request)
-      override;
-  void CreateGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
-                             const gfx::Size& size,
-                             gfx::BufferFormat format,
-                             gfx::BufferUsage usage,
-                             int client_id,
-                             gpu::SurfaceHandle surface_handle,
-                             CreateGpuMemoryBufferCallback callback) override;
-  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
-                              int client_id,
-                              const gpu::SyncToken& sync_token) override;
-  void GetVideoMemoryUsageStats(
-      GetVideoMemoryUsageStatsCallback callback) override;
-#if defined(OS_WIN)
-  void RequestCompleteGpuInfo(RequestCompleteGpuInfoCallback callback) override;
-  void GetGpuSupportedRuntimeVersion(
-      GetGpuSupportedRuntimeVersionCallback callback) override;
-#endif
-  void RequestHDRStatus(RequestHDRStatusCallback callback) override;
-  void LoadedShader(int32_t client_id,
-                    const std::string& key,
-                    const std::string& data) override;
-  void WakeUpGpu() override;
-  void GpuSwitched() override;
-  void DestroyAllChannels() override;
-  void OnBackgroundCleanup() override;
-  void OnBackgrounded() override;
-  void OnForegrounded() override;
-#if defined(OS_MACOSX)
-  void BeginCATransaction() override;
-  void CommitCATransaction(CommitCATransactionCallback callback) override;
-#endif
-  void Crash() override;
-  void Hang() override;
-  void ThrowJavaException() override;
-  void Stop(StopCallback callback) override;
-
 #if defined(OS_CHROMEOS)
   void CreateArcVideoDecodeAcceleratorOnMainThread(
       arc::mojom::VideoDecodeAcceleratorRequest vda_request);
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index a7673ea..db9dae3e 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1131,8 +1131,6 @@
     "media/android/media_player_renderer_web_contents_observer.h",
     "media/android/media_resource_getter_impl.cc",
     "media/android/media_resource_getter_impl.h",
-    "media/android/media_web_contents_observer_android.cc",
-    "media/android/media_web_contents_observer_android.h",
     "media/audible_metrics.cc",
     "media/audible_metrics.h",
     "media/audio_input_stream_broker.cc",
diff --git a/content/browser/loader/source_stream_to_data_pipe.cc b/content/browser/loader/source_stream_to_data_pipe.cc
index 56934492..c3923e59 100644
--- a/content/browser/loader/source_stream_to_data_pipe.cc
+++ b/content/browser/loader/source_stream_to_data_pipe.cc
@@ -65,16 +65,12 @@
 
 void SourceStreamToDataPipe::DidRead(int result) {
   DCHECK(pending_write_);
-  if (result < 0) {
-    // An error case.
+  if (result <= 0) {
+    // An error, or end of the stream.
+    pending_write_->Complete(0);  // Closes the data pipe.
     OnComplete(result);
     return;
   }
-  if (result == 0) {
-    pending_write_->Complete(0);
-    OnComplete(net::OK);
-    return;
-  }
   dest_ = pending_write_->Complete(result);
   pending_write_ = nullptr;
 
@@ -97,7 +93,7 @@
   // Resets the watchers, pipes and the exchange handler, so that
   // we will never be called back.
   writable_handle_watcher_.Cancel();
-  pending_write_ = nullptr;  // Closes the data pipe if this was holding it.
+  pending_write_ = nullptr;
   dest_.reset();
 
   std::move(completion_callback_).Run(result);
diff --git a/content/browser/media/android/media_web_contents_observer_android.cc b/content/browser/media/android/media_web_contents_observer_android.cc
deleted file mode 100644
index 1a2d7b8..0000000
--- a/content/browser/media/android/media_web_contents_observer_android.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/media/android/media_web_contents_observer_android.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/memory/ptr_util.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/common/media/media_player_delegate_messages.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "ipc/ipc_message_macros.h"
-
-namespace content {
-
-static void SuspendAllMediaPlayersInRenderFrame(
-    RenderFrameHost* render_frame_host) {
-  render_frame_host->Send(new MediaPlayerDelegateMsg_SuspendAllMediaPlayers(
-      render_frame_host->GetRoutingID()));
-}
-
-MediaWebContentsObserverAndroid::MediaWebContentsObserverAndroid(
-    WebContents* web_contents)
-    : MediaWebContentsObserver(web_contents) {}
-
-MediaWebContentsObserverAndroid::~MediaWebContentsObserverAndroid() {}
-
-// static
-MediaWebContentsObserverAndroid*
-MediaWebContentsObserverAndroid::FromWebContents(WebContents* web_contents) {
-  return static_cast<MediaWebContentsObserverAndroid*>(
-      static_cast<WebContentsImpl*>(web_contents)
-          ->media_web_contents_observer());
-}
-
-void MediaWebContentsObserverAndroid::SuspendAllMediaPlayers() {
-  web_contents()->ForEachFrame(
-      base::BindRepeating(&SuspendAllMediaPlayersInRenderFrame));
-}
-}  // namespace content
diff --git a/content/browser/media/android/media_web_contents_observer_android.h b/content/browser/media/android/media_web_contents_observer_android.h
deleted file mode 100644
index f09839ade..0000000
--- a/content/browser/media/android/media_web_contents_observer_android.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_MEDIA_ANDROID_MEDIA_WEB_CONTENTS_OBSERVER_ANDROID_H_
-#define CONTENT_BROWSER_MEDIA_ANDROID_MEDIA_WEB_CONTENTS_OBSERVER_ANDROID_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <unordered_map>
-
-#include "base/macros.h"
-#include "content/browser/media/media_web_contents_observer.h"
-#include "content/common/content_export.h"
-
-namespace media {
-enum class MediaContentType;
-}  // namespace media
-
-namespace content {
-
-// This class adds Android specific extensions to the MediaWebContentsObserver.
-class CONTENT_EXPORT MediaWebContentsObserverAndroid
-    : public MediaWebContentsObserver {
- public:
-  explicit MediaWebContentsObserverAndroid(WebContents* web_contents);
-  ~MediaWebContentsObserverAndroid() override;
-
-  // Returns the android specific observer for a given web contents.
-  static MediaWebContentsObserverAndroid* FromWebContents(
-      WebContents* web_contents);
-
-  // Called by the WebContents when a tab has been closed but may still be
-  // available for "undo" -- indicates that all media players (even audio only
-  // players typically allowed background audio) bound to this WebContents must
-  // be suspended.
-  void SuspendAllMediaPlayers();
- private:
-  DISALLOW_COPY_AND_ASSIGN(MediaWebContentsObserverAndroid);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_MEDIA_ANDROID_MEDIA_WEB_CONTENTS_OBSERVER_ANDROID_H_
diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc
index 740bdc6..b2b1f74 100644
--- a/content/browser/media/media_web_contents_observer.cc
+++ b/content/browser/media/media_web_contents_observer.cc
@@ -54,6 +54,14 @@
   return players->second.find(player_id.delegate_id) != players->second.end();
 }
 
+#if defined(OS_ANDROID)
+static void SuspendAllMediaPlayersInRenderFrame(
+    RenderFrameHost* render_frame_host) {
+  render_frame_host->Send(new MediaPlayerDelegateMsg_SuspendAllMediaPlayers(
+      render_frame_host->GetRoutingID()));
+}
+#endif  // defined(OS_ANDROID)
+
 }  // anonymous namespace
 
 MediaWebContentsObserver::MediaWebContentsObserver(WebContents* web_contents)
@@ -379,4 +387,11 @@
   return static_cast<WebContentsImpl*>(web_contents());
 }
 
+#if defined(OS_ANDROID)
+void MediaWebContentsObserver::SuspendAllMediaPlayers() {
+  web_contents()->ForEachFrame(
+      base::BindRepeating(&SuspendAllMediaPlayersInRenderFrame));
+}
+#endif  // defined(OS_ANDROID)
+
 }  // namespace content
diff --git a/content/browser/media/media_web_contents_observer.h b/content/browser/media/media_web_contents_observer.h
index 788c018..aaffbc3 100644
--- a/content/browser/media/media_web_contents_observer.h
+++ b/content/browser/media/media_web_contents_observer.h
@@ -12,6 +12,7 @@
 #include <set>
 
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "content/browser/media/session/media_session_controllers_manager.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -92,6 +93,13 @@
     audible_metrics_ = audible_metrics;
   }
 
+#if defined(OS_ANDROID)
+  // Called by the WebContents when a tab has been closed but may still be
+  // available for "undo" -- indicates that all media players (even audio only
+  // players typically allowed background audio) bound to this WebContents must
+  // be suspended.
+  void SuspendAllMediaPlayers();
+#endif  // defined(OS_ANDROID)
  protected:
   MediaSessionControllersManager* session_controllers_manager() {
     return &session_controllers_manager_;
diff --git a/content/browser/renderer_host/input/input_router_impl.cc b/content/browser/renderer_host/input/input_router_impl.cc
index f26efe9..04ee7dd 100644
--- a/content/browser/renderer_host/input/input_router_impl.cc
+++ b/content/browser/renderer_host/input/input_router_impl.cc
@@ -382,12 +382,9 @@
     touch_action_filter_.IncreaseActiveTouches();
     // In certain corner cases, the ack for the touch start may not come with a
     // touch action, then we should set the touch actions to Auto.
-    if ((compositor_touch_action_enabled_ &&
-         !touch_action_filter_.white_listed_touch_action().has_value()) ||
-        (!compositor_touch_action_enabled_ &&
-         !touch_action_filter_.allowed_touch_action().has_value())) {
+    if (!compositor_touch_action_enabled_ &&
+        !touch_action_filter_.allowed_touch_action().has_value()) {
       touch_action_filter_.OnSetTouchAction(cc::kTouchActionAuto);
-      touch_action_filter_.OnSetWhiteListedTouchAction(cc::kTouchActionAuto);
       if (compositor_touch_action_enabled_)
         touch_event_queue_.StopTimeoutMonitor();
       UpdateTouchAckTimeoutEnabled();
@@ -712,13 +709,12 @@
   // to page functionality, so the timeout could do more harm than good.
   base::Optional<cc::TouchAction> allowed_touch_action =
       touch_action_filter_.allowed_touch_action();
-  base::Optional<cc::TouchAction> white_listed_touch_action =
+  cc::TouchAction white_listed_touch_action =
       touch_action_filter_.white_listed_touch_action();
   const bool touch_ack_timeout_disabled =
       (allowed_touch_action.has_value() &&
        allowed_touch_action.value() == cc::kTouchActionNone) ||
-      (white_listed_touch_action.has_value() &&
-       white_listed_touch_action.value() == cc::kTouchActionNone);
+      (white_listed_touch_action == cc::kTouchActionNone);
   touch_event_queue_.SetAckTimeoutEnabled(!touch_ack_timeout_disabled);
 }
 
diff --git a/content/browser/renderer_host/input/input_router_impl_unittest.cc b/content/browser/renderer_host/input/input_router_impl_unittest.cc
index 6754d94..cee66ad8 100644
--- a/content/browser/renderer_host/input/input_router_impl_unittest.cc
+++ b/content/browser/renderer_host/input/input_router_impl_unittest.cc
@@ -480,7 +480,7 @@
                                    source, ack_state);
     EXPECT_EQ(input_router_->AllowedTouchAction(), expected_touch_action);
     EXPECT_EQ(input_router_->touch_action_filter_.white_listed_touch_action(),
-              expected_white_listed_touch_action);
+              expected_white_listed_touch_action.value());
   }
 
   const float radius_x_ = 20.0f;
@@ -512,7 +512,7 @@
     return input_router_->touch_action_filter_.allowed_touch_action_;
   }
 
-  base::Optional<cc::TouchAction> WhiteListedTouchAction() {
+  cc::TouchAction WhiteListedTouchAction() {
     return input_router_->touch_action_filter_.white_listed_touch_action_;
   }
 
@@ -714,58 +714,79 @@
   InputEventAckSource source = compositor_touch_action_enabled_
                                    ? InputEventAckSource::COMPOSITOR_THREAD
                                    : InputEventAckSource::MAIN_THREAD;
+  base::Optional<cc::TouchAction> expected_touch_action;
+  if (!compositor_touch_action_enabled_)
+    expected_touch_action = cc::kTouchActionAuto;
   OnTouchEventAckWithAckState(source, INPUT_EVENT_ACK_STATE_CONSUMED,
-                              cc::kTouchActionAuto, cc::kTouchActionAuto);
+                              expected_touch_action, cc::kTouchActionAuto);
 }
 
 TEST_P(InputRouterImplTest, TouchActionAutoWithAckStateNotConsumed) {
   InputEventAckSource source = compositor_touch_action_enabled_
                                    ? InputEventAckSource::COMPOSITOR_THREAD
                                    : InputEventAckSource::MAIN_THREAD;
+  base::Optional<cc::TouchAction> expected_touch_action;
+  if (!compositor_touch_action_enabled_)
+    expected_touch_action = cc::kTouchActionAuto;
   OnTouchEventAckWithAckState(source, INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
-                              cc::kTouchActionAuto, cc::kTouchActionAuto);
+                              expected_touch_action, cc::kTouchActionAuto);
 }
 
 TEST_P(InputRouterImplTest, TouchActionAutoWithAckStateConsumedShouldBubble) {
   InputEventAckSource source = compositor_touch_action_enabled_
                                    ? InputEventAckSource::COMPOSITOR_THREAD
                                    : InputEventAckSource::MAIN_THREAD;
+  base::Optional<cc::TouchAction> expected_touch_action;
+  if (!compositor_touch_action_enabled_)
+    expected_touch_action = cc::kTouchActionAuto;
   OnTouchEventAckWithAckState(source,
                               INPUT_EVENT_ACK_STATE_CONSUMED_SHOULD_BUBBLE,
-                              cc::kTouchActionAuto, cc::kTouchActionAuto);
+                              expected_touch_action, cc::kTouchActionAuto);
 }
 
 TEST_P(InputRouterImplTest, TouchActionAutoWithAckStateNoConsumerExists) {
   InputEventAckSource source = compositor_touch_action_enabled_
                                    ? InputEventAckSource::COMPOSITOR_THREAD
                                    : InputEventAckSource::MAIN_THREAD;
+  base::Optional<cc::TouchAction> expected_touch_action;
+  if (!compositor_touch_action_enabled_)
+    expected_touch_action = cc::kTouchActionAuto;
   OnTouchEventAckWithAckState(source, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS,
-                              cc::kTouchActionAuto, cc::kTouchActionAuto);
+                              expected_touch_action, cc::kTouchActionAuto);
 }
 
 TEST_P(InputRouterImplTest, TouchActionAutoWithAckStateIgnored) {
   InputEventAckSource source = compositor_touch_action_enabled_
                                    ? InputEventAckSource::COMPOSITOR_THREAD
                                    : InputEventAckSource::MAIN_THREAD;
+  base::Optional<cc::TouchAction> expected_touch_action;
+  if (!compositor_touch_action_enabled_)
+    expected_touch_action = cc::kTouchActionAuto;
   OnTouchEventAckWithAckState(source, INPUT_EVENT_ACK_STATE_IGNORED,
-                              cc::kTouchActionAuto, cc::kTouchActionAuto);
+                              expected_touch_action, cc::kTouchActionAuto);
 }
 
 TEST_P(InputRouterImplTest, TouchActionAutoWithAckStateNonBlocking) {
   InputEventAckSource source = compositor_touch_action_enabled_
                                    ? InputEventAckSource::COMPOSITOR_THREAD
                                    : InputEventAckSource::MAIN_THREAD;
+  base::Optional<cc::TouchAction> expected_touch_action;
+  if (!compositor_touch_action_enabled_)
+    expected_touch_action = cc::kTouchActionAuto;
   OnTouchEventAckWithAckState(source, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING,
-                              cc::kTouchActionAuto, cc::kTouchActionAuto);
+                              expected_touch_action, cc::kTouchActionAuto);
 }
 
 TEST_P(InputRouterImplTest, TouchActionAutoWithAckStateNonBlockingDueToFling) {
   InputEventAckSource source = compositor_touch_action_enabled_
                                    ? InputEventAckSource::COMPOSITOR_THREAD
                                    : InputEventAckSource::MAIN_THREAD;
+  base::Optional<cc::TouchAction> expected_touch_action;
+  if (!compositor_touch_action_enabled_)
+    expected_touch_action = cc::kTouchActionAuto;
   OnTouchEventAckWithAckState(
       source, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING_DUE_TO_FLING,
-      cc::kTouchActionAuto, cc::kTouchActionAuto);
+      expected_touch_action, cc::kTouchActionAuto);
 }
 
 // Tests that touch-events are sent properly.
@@ -2087,13 +2108,11 @@
       expected_touch_action);
   ASSERT_EQ(1U, disposition_handler_->GetAndResetAckCount());
   base::Optional<cc::TouchAction> allowed_touch_action = AllowedTouchAction();
-  base::Optional<cc::TouchAction> white_listed_touch_action =
-      WhiteListedTouchAction();
+  cc::TouchAction white_listed_touch_action = WhiteListedTouchAction();
   if (compositor_touch_action_enabled_) {
     EXPECT_FALSE(allowed_touch_action.has_value());
-    EXPECT_EQ(expected_touch_action, white_listed_touch_action);
+    EXPECT_EQ(expected_touch_action.value(), white_listed_touch_action);
   } else {
-    EXPECT_FALSE(white_listed_touch_action.has_value());
     EXPECT_EQ(expected_touch_action, allowed_touch_action);
   }
 }
diff --git a/content/browser/renderer_host/input/touch_action_filter.cc b/content/browser/renderer_host/input/touch_action_filter.cc
index 6cacb57..8234734 100644
--- a/content/browser/renderer_host/input/touch_action_filter.cc
+++ b/content/browser/renderer_host/input/touch_action_filter.cc
@@ -137,16 +137,9 @@
     return FilterGestureEventResult::kFilterGestureEventDelayed;
   }
 
-  base::Optional<cc::TouchAction> touch_action;
-  if (gesture_event->GetType() == WebInputEvent::kGestureTapDown) {
-    touch_action = allowed_touch_action_.has_value()
-                       ? allowed_touch_action_
-                       : white_listed_touch_action_;
-  } else {
-    touch_action = active_touch_action_.has_value()
-                       ? active_touch_action_
-                       : white_listed_touch_action_;
-  }
+  cc::TouchAction touch_action = active_touch_action_.has_value()
+                                     ? active_touch_action_.value()
+                                     : white_listed_touch_action_;
 
   // Filter for allowable touch actions first (eg. before the TouchEventQueue
   // can decide to send a touch cancel event).
@@ -156,9 +149,13 @@
       // GestureScrollBegin could come without GestureTapDown.
       if (!gesture_sequence_in_progress_) {
         gesture_sequence_in_progress_ = true;
-        if (!active_touch_action_.has_value()) {
-          SetTouchAction(cc::kTouchActionAuto);
-          touch_action = cc::kTouchActionAuto;
+        if (allowed_touch_action_.has_value()) {
+          active_touch_action_ = allowed_touch_action_;
+          touch_action = allowed_touch_action_.value();
+        } else {
+          touch_action = compositor_touch_action_enabled_
+                             ? white_listed_touch_action_
+                             : active_touch_action_.value();
         }
       }
       gesture_sequence_.append("B");
@@ -169,16 +166,8 @@
         base::debug::SetCrashKeyString(crash_key, gesture_sequence_);
         gesture_sequence_.clear();
       }
-      if (compositor_touch_action_enabled_ && !touch_action.has_value()) {
-        static auto* crash_key = base::debug::AllocateCrashKeyString(
-            "scrollbegin1-gestures", base::debug::CrashKeySize::Size256);
-        base::debug::SetCrashKeyString(crash_key, gesture_sequence_);
-        base::debug::DumpWithoutCrashing();
-        gesture_sequence_.clear();
-      }
-      DCHECK(touch_action.has_value());
       drop_scroll_events_ =
-          ShouldSuppressScrolling(*gesture_event, touch_action.value());
+          ShouldSuppressScrolling(*gesture_event, touch_action);
       FilterGestureEventResult res;
       if (!drop_scroll_events_) {
         res = FilterGestureEventResult::kFilterGestureEventAllowed;
@@ -218,7 +207,7 @@
         base::debug::SetCrashKeyString(crash_key, gesture_sequence_);
         gesture_sequence_.clear();
       }
-      if (IsYAxisActionDisallowed(touch_action.value())) {
+      if (IsYAxisActionDisallowed(touch_action)) {
         if (compositor_touch_action_enabled_ &&
             !active_touch_action_.has_value() &&
             gesture_event->data.scroll_update.delta_y != 0) {
@@ -230,7 +219,7 @@
         }
         gesture_event->data.scroll_update.delta_y = 0;
         gesture_event->data.scroll_update.velocity_y = 0;
-      } else if (IsXAxisActionDisallowed(touch_action.value())) {
+      } else if (IsXAxisActionDisallowed(touch_action)) {
         if (compositor_touch_action_enabled_ &&
             !active_touch_action_.has_value() &&
             gesture_event->data.scroll_update.delta_x != 0) {
@@ -259,18 +248,18 @@
       if (gesture_sequence_.size() >= 1000)
         gesture_sequence_.erase(gesture_sequence_.begin(),
                                 gesture_sequence_.end() - 250);
+      // Do not reset |white_listed_touch_action_|. In the fling cancel case,
+      // the ack for the second touch sequence start, which sets the white
+      // listed touch action, could arrive before the GSE of the first fling
+      // sequence, we do not want to reset the white listed touch action.
       gesture_sequence_in_progress_ = false;
-      // Whenever there is a new touch start, white listed touch action will be
-      // set. So it is fine to reset at GSE.
-      white_listed_touch_action_.reset();
       ReportGestureEventFiltered(drop_scroll_events_);
       return FilterScrollEventAndResetState();
 
     // Evaluate the |drop_pinch_events_| here instead of GSB because pinch
     // events could arrive without GSB, e.g. double-tap-drag.
     case WebInputEvent::kGesturePinchBegin:
-      drop_pinch_events_ =
-          (touch_action.value() & cc::kTouchActionPinchZoom) == 0;
+      drop_pinch_events_ = (touch_action & cc::kTouchActionPinchZoom) == 0;
       FALLTHROUGH;
     case WebInputEvent::kGesturePinchUpdate:
       gesture_sequence_.append("P");
@@ -313,7 +302,7 @@
         gesture_sequence_.clear();
       }
       allow_current_double_tap_event_ =
-          (touch_action.value() & cc::kTouchActionDoubleTapZoom) != 0;
+          (touch_action & cc::kTouchActionDoubleTapZoom) != 0;
       if (!allow_current_double_tap_event_) {
         gesture_event->SetType(WebInputEvent::kGestureTap);
         drop_current_tap_ending_event_ = true;
@@ -344,29 +333,10 @@
         gesture_sequence_.append("AY");
       else
         gesture_sequence_.append("AN");
-      if (white_listed_touch_action_.has_value())
-        gesture_sequence_.append("WY");
-      else
-        gesture_sequence_.append("WN");
       if (active_touch_action_.has_value())
         gesture_sequence_.append("OY");
       else
         gesture_sequence_.append("ON");
-      if (compositor_touch_action_enabled_ &&
-          !allowed_touch_action_.has_value() &&
-          !white_listed_touch_action_.has_value()) {
-        static auto* crash_key = base::debug::AllocateCrashKeyString(
-            "tapdown-gestures", base::debug::CrashKeySize::Size256);
-        if (gesture_sequence_.size() >= 256)
-          gesture_sequence_.erase(gesture_sequence_.begin(),
-                                  gesture_sequence_.end() - 256);
-        base::debug::SetCrashKeyString(crash_key, gesture_sequence_);
-        base::debug::DumpWithoutCrashing();
-        gesture_sequence_.clear();
-      }
-      // TODO(xidachen): investigate why the touch action has no value.
-      if (compositor_touch_action_enabled_ && !touch_action.has_value())
-        SetTouchAction(cc::kTouchActionAuto);
       // In theory, the num_of_active_touches_ should be > 0 at this point. But
       // crash reports suggest otherwise.
       if (num_of_active_touches_ <= 0)
@@ -481,11 +451,9 @@
   // Report how often the effective touch action computed by blink is or is
   // not equivalent to the whitelisted touch action computed by the
   // compositor.
-  if (white_listed_touch_action_.has_value()) {
-    UMA_HISTOGRAM_BOOLEAN(
-        "TouchAction.EquivalentEffectiveAndWhiteListed",
-        active_touch_action_.value() == white_listed_touch_action_.value());
-  }
+  UMA_HISTOGRAM_BOOLEAN(
+      "TouchAction.EquivalentEffectiveAndWhiteListed",
+      active_touch_action_.value() == white_listed_touch_action_);
 }
 
 void TouchActionFilter::AppendToGestureSequenceForDebugging(const char* str) {
@@ -498,6 +466,7 @@
   // sequenceo.
   if (has_touch_event_handler_) {
     allowed_touch_action_.reset();
+    white_listed_touch_action_ = cc::kTouchActionAuto;
   } else {
     // Lack of a touch handler indicates that the page either has no
     // touch-action modifiers or that all its touch-action modifiers are auto.
@@ -511,12 +480,8 @@
     cc::TouchAction white_listed_touch_action) {
   // We use '&' here to account for the multiple-finger case, which is the same
   // as OnSetTouchAction.
-  if (white_listed_touch_action_.has_value()) {
-    white_listed_touch_action_ =
-        white_listed_touch_action_.value() & white_listed_touch_action;
-  } else {
-    white_listed_touch_action_ = white_listed_touch_action;
-  }
+  white_listed_touch_action_ =
+      white_listed_touch_action_ & white_listed_touch_action;
 }
 
 bool TouchActionFilter::ShouldSuppressScrolling(
@@ -577,7 +542,6 @@
     if (has_touch_event_handler_) {
       gesture_sequence_.append("H");
       active_touch_action_.reset();
-      white_listed_touch_action_.reset();
     }
   }
 }
diff --git a/content/browser/renderer_host/input/touch_action_filter.h b/content/browser/renderer_host/input/touch_action_filter.h
index e048fa76..f1d5f6a 100644
--- a/content/browser/renderer_host/input/touch_action_filter.h
+++ b/content/browser/renderer_host/input/touch_action_filter.h
@@ -59,7 +59,7 @@
     return allowed_touch_action_;
   }
 
-  base::Optional<cc::TouchAction> white_listed_touch_action() const {
+  cc::TouchAction white_listed_touch_action() const {
     return white_listed_touch_action_;
   }
 
@@ -136,10 +136,8 @@
   // sequence due to fling.
   base::Optional<cc::TouchAction> active_touch_action_;
 
-  // TODO(xidachen): consider giving this a default value of Auto, instead of
-  // Optional.
   // Whitelisted touch action received from the compositor.
-  base::Optional<cc::TouchAction> white_listed_touch_action_;
+  cc::TouchAction white_listed_touch_action_;
 
   // Debugging only.
   std::string gesture_sequence_;
diff --git a/content/browser/renderer_host/input/touch_action_filter_unittest.cc b/content/browser/renderer_host/input/touch_action_filter_unittest.cc
index a9ce1d2..7d3b4983 100644
--- a/content/browser/renderer_host/input/touch_action_filter_unittest.cc
+++ b/content/browser/renderer_host/input/touch_action_filter_unittest.cc
@@ -44,7 +44,7 @@
   void ResetTouchAction() { filter_.ResetTouchAction(); }
   void ResetActiveTouchAction() { filter_.active_touch_action_.reset(); }
   void ResetWhiteListedTouchAction() {
-    filter_.white_listed_touch_action_.reset();
+    filter_.white_listed_touch_action_ = cc::kTouchActionAuto;
   }
   void SetNoDeferredEvents() { filter_.has_deferred_events_ = false; }
   void SetGestureSequenceInProgress() {
@@ -1202,7 +1202,6 @@
   filter_.OnHasTouchEventHandlers(true);
   EXPECT_FALSE(ActiveTouchAction().has_value());
   EXPECT_FALSE(filter_.allowed_touch_action().has_value());
-  EXPECT_FALSE(filter_.white_listed_touch_action().has_value());
 
   int dx = 2, dy = 5;
   // Test gestures that are allowed.
@@ -1217,7 +1216,7 @@
   filter_.OnSetWhiteListedTouchAction(cc::kTouchActionPan);
   if (!compositor_touch_action_enabled_)
     filter_.OnSetTouchAction(cc::kTouchActionPan);
-  EXPECT_EQ(filter_.white_listed_touch_action().value(), cc::kTouchActionPan);
+  EXPECT_EQ(filter_.white_listed_touch_action(), cc::kTouchActionPan);
   SetGestureSequenceInProgress();
   EXPECT_EQ(filter_.FilterGestureEvent(&scroll_begin),
             FilterGestureEventResult::kFilterGestureEventAllowed);
@@ -1233,7 +1232,7 @@
   filter_.OnSetWhiteListedTouchAction(cc::kTouchActionPan);
   if (!compositor_touch_action_enabled_)
     filter_.OnSetTouchAction(cc::kTouchActionPan);
-  EXPECT_EQ(filter_.white_listed_touch_action().value(), cc::kTouchActionPan);
+  EXPECT_EQ(filter_.white_listed_touch_action(), cc::kTouchActionPan);
   WebGestureEvent pinch_begin = SyntheticWebGestureEventBuilder::Build(
       WebInputEvent::kGesturePinchBegin, kSourceDevice);
   WebGestureEvent pinch_update =
@@ -1266,7 +1265,7 @@
   if (!compositor_touch_action_enabled_)
     filter_.OnSetTouchAction(cc::kTouchActionPanY);
   SetNoDeferredEvents();
-  EXPECT_EQ(filter_.white_listed_touch_action().value(), cc::kTouchActionPanY);
+  EXPECT_EQ(filter_.white_listed_touch_action(), cc::kTouchActionPanY);
   SetGestureSequenceInProgress();
   EXPECT_EQ(filter_.FilterGestureEvent(&scroll_begin),
             FilterGestureEventResult::kFilterGestureEventAllowed);
@@ -1289,7 +1288,7 @@
   if (!compositor_touch_action_enabled_)
     filter_.OnSetTouchAction(cc::kTouchActionPanX);
   SetNoDeferredEvents();
-  EXPECT_EQ(filter_.white_listed_touch_action().value(), cc::kTouchActionPanX);
+  EXPECT_EQ(filter_.white_listed_touch_action(), cc::kTouchActionPanX);
 
   dy = 0;
   scroll_begin =
@@ -1311,7 +1310,7 @@
   scroll_update = SyntheticWebGestureEventBuilder::BuildScrollUpdate(
       dx, dy, 0, kSourceDevice);
   filter_.OnSetWhiteListedTouchAction(cc::kTouchActionPanX);
-  EXPECT_EQ(filter_.white_listed_touch_action().value(), cc::kTouchActionPanX);
+  EXPECT_EQ(filter_.white_listed_touch_action(), cc::kTouchActionPanX);
   SetGestureSequenceInProgress();
   if (compositor_touch_action_enabled_) {
     EXPECT_EQ(filter_.FilterGestureEvent(&scroll_begin),
@@ -1330,22 +1329,21 @@
   }
 }
 
-TEST_P(TouchActionFilterTest, WhiteListedTouchActionNotResetHasHandlers) {
+TEST_P(TouchActionFilterTest, WhiteListedTouchActionResetToAuto) {
   filter_.OnHasTouchEventHandlers(true);
-  EXPECT_FALSE(filter_.white_listed_touch_action().has_value());
 
   filter_.OnSetWhiteListedTouchAction(cc::kTouchActionPan);
-  EXPECT_EQ(filter_.white_listed_touch_action().value(), cc::kTouchActionPan);
+  EXPECT_EQ(filter_.white_listed_touch_action(), cc::kTouchActionPan);
   ResetTouchAction();
-  EXPECT_EQ(filter_.white_listed_touch_action().value(), cc::kTouchActionPan);
+  EXPECT_EQ(filter_.white_listed_touch_action(), cc::kTouchActionAuto);
 }
 
 TEST_P(TouchActionFilterTest, WhiteListedTouchActionAutoNoHasHandlers) {
   filter_.OnHasTouchEventHandlers(false);
-  EXPECT_EQ(filter_.white_listed_touch_action().value(), cc::kTouchActionAuto);
+  EXPECT_EQ(filter_.white_listed_touch_action(), cc::kTouchActionAuto);
 
   ResetTouchAction();
-  EXPECT_EQ(filter_.white_listed_touch_action().value(), cc::kTouchActionAuto);
+  EXPECT_EQ(filter_.white_listed_touch_action(), cc::kTouchActionAuto);
 }
 
 TEST_P(TouchActionFilterTest, ResetBeforeHasHandlerSet) {
@@ -1357,6 +1355,35 @@
             FilterGestureEventResult::kFilterGestureEventAllowed);
 }
 
+TEST_P(TouchActionFilterTest,
+       WhiteListedTouchActionNotResetAtGestureScrollEnd) {
+  if (!compositor_touch_action_enabled_)
+    return;
+  filter_.OnHasTouchEventHandlers(true);
+
+  filter_.OnSetWhiteListedTouchAction(cc::kTouchActionPan);
+  EXPECT_EQ(filter_.white_listed_touch_action(), cc::kTouchActionPan);
+
+  int dx = 2, dy = 5;
+  WebGestureEvent scroll_begin =
+      SyntheticWebGestureEventBuilder::BuildScrollBegin(dx, dy, kSourceDevice);
+  WebGestureEvent scroll_update =
+      SyntheticWebGestureEventBuilder::BuildScrollUpdate(dx, dy, 0,
+                                                         kSourceDevice);
+  WebGestureEvent scroll_end = SyntheticWebGestureEventBuilder::Build(
+      WebInputEvent::kGestureScrollEnd, kSourceDevice);
+
+  SetGestureSequenceInProgress();
+  EXPECT_EQ(filter_.FilterGestureEvent(&scroll_begin),
+            FilterGestureEventResult::kFilterGestureEventAllowed);
+  EXPECT_EQ(filter_.FilterGestureEvent(&scroll_update),
+            FilterGestureEventResult::kFilterGestureEventAllowed);
+  EXPECT_EQ(filter_.FilterGestureEvent(&scroll_end),
+            FilterGestureEventResult::kFilterGestureEventAllowed);
+
+  EXPECT_EQ(filter_.white_listed_touch_action(), cc::kTouchActionPan);
+}
+
 // Having a gesture scroll begin without tap down should set touch action to
 // Auto.
 TEST_P(TouchActionFilterTest, ScrollBeginWithoutTapDown) {
@@ -1364,12 +1391,20 @@
   EXPECT_FALSE(ActiveTouchAction().has_value());
   EXPECT_FALSE(filter_.allowed_touch_action().has_value());
 
+  if (compositor_touch_action_enabled_)
+    filter_.OnSetWhiteListedTouchAction(cc::kTouchActionPan);
+  else
+    filter_.OnSetTouchAction(cc::kTouchActionPan);
   WebGestureEvent scroll_begin =
       SyntheticWebGestureEventBuilder::BuildScrollBegin(5, 0, kSourceDevice);
   EXPECT_EQ(filter_.FilterGestureEvent(&scroll_begin),
             FilterGestureEventResult::kFilterGestureEventAllowed);
-  EXPECT_EQ(ActiveTouchAction().value(), cc::kTouchActionAuto);
-  EXPECT_EQ(filter_.allowed_touch_action().value(), cc::kTouchActionAuto);
+  if (compositor_touch_action_enabled_) {
+    EXPECT_EQ(filter_.white_listed_touch_action(), cc::kTouchActionPan);
+  } else {
+    EXPECT_EQ(ActiveTouchAction().value(), cc::kTouchActionPan);
+    EXPECT_EQ(filter_.allowed_touch_action().value(), cc::kTouchActionPan);
+  }
 }
 
 // This tests a gesture tap down with |num_of_active_touches_| == 0
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 2ccad70..a8df14b 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -364,7 +364,8 @@
   params->opener_frame_route_id = opener_frame_route_id;
   params->replicated_frame_state = replicated_frame_state;
   params->proxy_routing_id = proxy_route_id;
-  params->hidden = GetWidget()->delegate()->IsHidden();
+  params->hidden = is_active() ? GetWidget()->is_hidden()
+                               : GetWidget()->delegate()->IsHidden();
   params->never_visible = delegate_->IsNeverVisible();
   params->window_was_created_with_opener = window_was_created_with_opener;
   if (main_rfh) {
diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h
index 25d59c5b..688a332 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.h
+++ b/content/browser/renderer_host/render_widget_host_delegate.h
@@ -249,7 +249,6 @@
   // the RenderWidgetHost to ask the delegate if it can be shown in the event of
   // something other than the WebContents attempting to enable visibility of
   // this RenderWidgetHost.
-  // TODO(nasko): Move this to RenderViewHostDelegate.
   virtual bool IsHidden();
 
   // Returns the associated RenderViewHostDelegateView*, if possible.
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 93a418bcb..2baa5daf 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -46,7 +46,6 @@
 #include "content/browser/compositor/surface_utils.h"
 #include "content/browser/devtools/render_frame_devtools_agent_host.h"
 #include "content/browser/gpu/gpu_process_host.h"
-#include "content/browser/media/android/media_web_contents_observer_android.h"
 #include "content/browser/renderer_host/compositor_impl_android.h"
 #include "content/browser/renderer_host/delegated_frame_host_client_android.h"
 #include "content/browser/renderer_host/dip_util.h"
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 30360e3..2bc40d1 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -12412,7 +12412,6 @@
             ->input_router());
     // Clear the touch actions that were set by previous touches.
     input_router->touch_action_filter_.allowed_touch_action_.reset();
-    input_router->touch_action_filter_.white_listed_touch_action_.reset();
     // Send a touch start event to child to get the TAF filled with child
     // frame's touch action.
     ack_observer.Reset();
diff --git a/content/browser/web_contents/web_contents_android.cc b/content/browser/web_contents/web_contents_android.cc
index 777bee8..b724462 100644
--- a/content/browser/web_contents/web_contents_android.cc
+++ b/content/browser/web_contents/web_contents_android.cc
@@ -26,7 +26,7 @@
 #include "content/browser/accessibility/browser_accessibility_manager_android.h"
 #include "content/browser/android/java/gin_java_bridge_dispatcher_host.h"
 #include "content/browser/frame_host/interstitial_page_impl.h"
-#include "content/browser/media/android/media_web_contents_observer_android.h"
+#include "content/browser/media/media_web_contents_observer.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/web_contents/web_contents_view_android.h"
@@ -426,8 +426,7 @@
 void WebContentsAndroid::SuspendAllMediaPlayers(
     JNIEnv* env,
     const JavaParamRef<jobject>& jobj) {
-  MediaWebContentsObserverAndroid::FromWebContents(web_contents_)
-      ->SuspendAllMediaPlayers();
+  web_contents_->media_web_contents_observer()->SuspendAllMediaPlayers();
 }
 
 void WebContentsAndroid::SetAudioMuted(JNIEnv* env,
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 613d5ff1..e87aa15 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -170,7 +170,6 @@
 #if defined(OS_ANDROID)
 #include "content/browser/android/date_time_chooser_android.h"
 #include "content/browser/android/java_interfaces_impl.h"
-#include "content/browser/media/android/media_web_contents_observer_android.h"
 #include "content/browser/web_contents/web_contents_android.h"
 #include "services/device/public/mojom/nfc.mojom.h"
 #else  // !OS_ANDROID
@@ -592,6 +591,8 @@
               browser_context)),
       audio_stream_monitor_(this),
       bluetooth_connected_device_count_(0),
+      media_web_contents_observer_(
+          std::make_unique<MediaWebContentsObserver>(this)),
       media_device_group_id_salt_base_(
           BrowserContext::CreateRandomMediaDeviceIDSalt()),
 #if !defined(OS_ANDROID)
@@ -604,11 +605,6 @@
   frame_tree_.SetFrameRemoveListener(
       base::Bind(&WebContentsImpl::OnFrameRemoved,
                  base::Unretained(this)));
-#if defined(OS_ANDROID)
-  media_web_contents_observer_.reset(new MediaWebContentsObserverAndroid(this));
-#else
-  media_web_contents_observer_.reset(new MediaWebContentsObserver(this));
-#endif
 #if BUILDFLAG(ENABLE_PLUGINS)
   pepper_playback_observer_.reset(new PepperPlaybackObserver(this));
 #endif
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 35fe0e6..a5153dc 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -3242,17 +3242,4 @@
   EXPECT_EQ(new_web_contents1, new_web_contents2);
 }
 
-IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, SetVisibilityBeforeLoad) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-  GURL url(embedded_test_server()->GetURL("/hello.html"));
-
-  WebContentsImpl* web_contents =
-      static_cast<WebContentsImpl*>(shell()->web_contents());
-  web_contents->WasHidden();
-  TestNavigationObserver same_tab_observer(web_contents);
-  shell()->LoadURL(url);
-  same_tab_observer.Wait();
-  EXPECT_TRUE(EvalJs(shell(), "document.hidden").ExtractBool());
-}
-
 }  // namespace content
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index dfde858..61b18ad 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -107,6 +107,8 @@
     "cookie_store_factory.h",
     "cors_exempt_headers.cc",
     "cors_exempt_headers.h",
+    "cors_origin_pattern_setter.cc",
+    "cors_origin_pattern_setter.h",
     "delegate_to_browser_gpu_service_accelerator_factory.h",
     "desktop_capture.cc",
     "desktop_capture.h",
diff --git a/content/public/browser/cors_origin_pattern_setter.cc b/content/public/browser/cors_origin_pattern_setter.cc
new file mode 100644
index 0000000..27b4798f
--- /dev/null
+++ b/content/public/browser/cors_origin_pattern_setter.cc
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/browser/cors_origin_pattern_setter.h"
+#include "content/public/browser/storage_partition.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+
+namespace content {
+
+CorsOriginPatternSetter::CorsOriginPatternSetter(
+    const url::Origin& source_origin,
+    std::vector<network::mojom::CorsOriginPatternPtr> allow_patterns,
+    std::vector<network::mojom::CorsOriginPatternPtr> block_patterns,
+    base::OnceClosure closure)
+    : source_origin_(source_origin),
+      allow_patterns_(std::move(allow_patterns)),
+      block_patterns_(std::move(block_patterns)),
+      closure_(std::move(closure)) {}
+
+CorsOriginPatternSetter::~CorsOriginPatternSetter() {
+  std::move(closure_).Run();
+}
+
+void CorsOriginPatternSetter::SetLists(StoragePartition* partition) {
+  partition->GetNetworkContext()->SetCorsOriginAccessListsForOrigin(
+      source_origin_, ClonePatterns(allow_patterns_),
+      ClonePatterns(block_patterns_),
+      base::BindOnce([](scoped_refptr<CorsOriginPatternSetter> setter) {},
+                     base::RetainedRef(this)));
+}
+
+// static
+std::vector<network::mojom::CorsOriginPatternPtr>
+CorsOriginPatternSetter::ClonePatterns(
+    const std::vector<network::mojom::CorsOriginPatternPtr>& patterns) {
+  std::vector<network::mojom::CorsOriginPatternPtr> cloned_patterns;
+  cloned_patterns.reserve(patterns.size());
+  for (const auto& item : patterns)
+    cloned_patterns.push_back(item.Clone());
+  return cloned_patterns;
+}
+
+}  // namespace content
diff --git a/content/public/browser/cors_origin_pattern_setter.h b/content/public/browser/cors_origin_pattern_setter.h
new file mode 100644
index 0000000..da2b156
--- /dev/null
+++ b/content/public/browser/cors_origin_pattern_setter.h
@@ -0,0 +1,59 @@
+// 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_PUBLIC_BROWSER_CORS_ORIGIN_PATTERN_SETTER_H_
+#define CONTENT_PUBLIC_BROWSER_CORS_ORIGIN_PATTERN_SETTER_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/barrier_closure.h"
+#include "base/callback.h"
+#include "content/common/content_export.h"
+#include "services/network/public/mojom/cors_origin_pattern.mojom.h"
+#include "url/origin.h"
+
+namespace content {
+
+class StoragePartition;
+
+// A class used to make an asynchronous Mojo call with cloned patterns for each
+// StoragePartition iteration. |this| instance will be destructed when all
+// existing asynchronous Mojo calls made in SetLists() are done, and |closure|
+// will be invoked on destructing |this|.
+//
+// Typically this would be used to implement
+// BrowserContext::SetCorsOriginAccessListForOrigin, and would use
+// ForEachStoragePartition with SetLists as the StoragePartitionCallback.
+class CONTENT_EXPORT CorsOriginPatternSetter
+    : public base::RefCounted<CorsOriginPatternSetter> {
+ public:
+  CorsOriginPatternSetter(
+      const url::Origin& source_origin,
+      std::vector<network::mojom::CorsOriginPatternPtr> allow_patterns,
+      std::vector<network::mojom::CorsOriginPatternPtr> block_patterns,
+      base::OnceClosure closure);
+
+  void SetLists(StoragePartition* partition);
+
+  static std::vector<network::mojom::CorsOriginPatternPtr> ClonePatterns(
+      const std::vector<network::mojom::CorsOriginPatternPtr>& patterns);
+
+ private:
+  friend class base::RefCounted<CorsOriginPatternSetter>;
+
+  ~CorsOriginPatternSetter();
+
+  const url::Origin source_origin_;
+  const std::vector<network::mojom::CorsOriginPatternPtr> allow_patterns_;
+  const std::vector<network::mojom::CorsOriginPatternPtr> block_patterns_;
+
+  base::OnceClosure closure_;
+
+  DISALLOW_COPY_AND_ASSIGN(CorsOriginPatternSetter);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_CORS_ORIGIN_PATTERN_SETTER_H_
diff --git a/content/public/test/blink_test_environment.h b/content/public/test/blink_test_environment.h
index da56a64..c973147 100644
--- a/content/public/test/blink_test_environment.h
+++ b/content/public/test/blink_test_environment.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_PUBLIC_TEST_BLINK_TEST_ENVIRONMENT_H_
 #define CONTENT_PUBLIC_TEST_BLINK_TEST_ENVIRONMENT_H_
 
-// This package provides functions used by webkit_unit_tests.
+// This package provides functions used by blink_unittests.
 namespace content {
 
 // Initializes Blink test environment for unit tests.
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.cc b/content/renderer/loader/web_worker_fetch_context_impl.cc
index ac9541f..72b0f020 100644
--- a/content/renderer/loader/web_worker_fetch_context_impl.cc
+++ b/content/renderer/loader/web_worker_fetch_context_impl.cc
@@ -262,7 +262,7 @@
       mojo::MakeRequest(&service_worker_worker_client_registry_ptr_info));
 
   blink::mojom::ServiceWorkerContainerHostPtrInfo host_ptr_info;
-  if (blink::ServiceWorkerUtils::IsServicificationEnabled()) {
+  if (service_worker_container_host_) {
     service_worker_container_host_->CloneContainerHost(
         mojo::MakeRequest(&host_ptr_info));
   }
@@ -317,8 +317,10 @@
     preference_watcher_binding_.Bind(std::move(preference_watcher_request_));
 
   if (blink::ServiceWorkerUtils::IsServicificationEnabled()) {
-    service_worker_container_host_.Bind(
-        std::move(service_worker_container_host_info_));
+    if (service_worker_container_host_info_) {
+      service_worker_container_host_.Bind(
+          std::move(service_worker_container_host_info_));
+    }
 
     blink::mojom::BlobRegistryPtr blob_registry_ptr;
     service_manager_connection_->BindInterface(
@@ -533,6 +535,8 @@
     web_loader_factory_->SetServiceWorkerURLLoaderFactory(nullptr);
     return;
   }
+  if (!service_worker_container_host_)
+    return;
 
   network::mojom::URLLoaderFactoryPtr service_worker_url_loader_factory;
   blink::mojom::ServiceWorkerContainerHostPtrInfo host_ptr_info;
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.h b/content/renderer/loader/web_worker_fetch_context_impl.h
index 7bebcc5..b4beeb8 100644
--- a/content/renderer/loader/web_worker_fetch_context_impl.h
+++ b/content/renderer/loader/web_worker_fetch_context_impl.h
@@ -203,6 +203,8 @@
 
   // S13nServiceWorker:
   // Initialized on the worker thread when InitializeOnWorkerThread() is called.
+  // May be nullptr if the creating context was already being destructed, see
+  // ServiceWorkerProviderContext::OnNetworkProviderDestroyed().
   blink::mojom::ServiceWorkerContainerHostPtr service_worker_container_host_;
 
   // S13nServiceWorker:
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index 2d52883..6b0a942 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -110,7 +110,7 @@
 // establishing a GPUChannelHost, which must be done on the main thread.
 void PostContextProviderToCallback(
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
-    scoped_refptr<viz::ContextProvider> unwanted_context_provider,
+    scoped_refptr<viz::RasterContextProvider> unwanted_context_provider,
     blink::WebSubmitterConfigurationCallback set_context_provider_callback) {
   // |unwanted_context_provider| needs to be destroyed on the current thread.
   // Therefore, post a reply-callback that retains a reference to it, so that it
@@ -118,7 +118,8 @@
   main_task_runner->PostTaskAndReply(
       FROM_HERE,
       base::BindOnce(
-          [](scoped_refptr<viz::ContextProvider> unwanted_context_provider,
+          [](scoped_refptr<viz::RasterContextProvider>
+                 unwanted_context_provider,
              blink::WebSubmitterConfigurationCallback cb) {
             auto* rti = content::RenderThreadImpl::current();
             auto context_provider = rti->GetVideoFrameCompositorContextProvider(
@@ -128,9 +129,9 @@
           },
           unwanted_context_provider,
           media::BindToCurrentLoop(std::move(set_context_provider_callback))),
-      base::BindOnce(
-          [](scoped_refptr<viz::ContextProvider> unwanted_context_provider) {},
-          unwanted_context_provider));
+      base::BindOnce([](scoped_refptr<viz::RasterContextProvider>
+                            unwanted_context_provider) {},
+                     unwanted_context_provider));
 }
 
 }  // namespace
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index f426c57..b5669f9 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1370,9 +1370,9 @@
   return gpu_factories_.back().get();
 }
 
-scoped_refptr<viz::ContextProvider>
+scoped_refptr<viz::RasterContextProvider>
 RenderThreadImpl::GetVideoFrameCompositorContextProvider(
-    scoped_refptr<viz::ContextProvider> unwanted_context_provider) {
+    scoped_refptr<viz::RasterContextProvider> unwanted_context_provider) {
   DCHECK(video_frame_compositor_task_runner_);
   if (video_frame_compositor_context_provider_ &&
       video_frame_compositor_context_provider_ != unwanted_context_provider) {
@@ -1396,7 +1396,7 @@
 
   bool support_locking = false;
   bool support_gles2_interface = true;
-  bool support_raster_interface = false;
+  bool support_raster_interface = true;
   bool support_oop_rasterization = false;
   bool support_grcontext = false;
   bool automatic_flushes = false;
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index 5a5b774..579131f 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -385,8 +385,9 @@
   // video frame compositing. The ContextProvider given as an argument is
   // one that has been lost, and is a hint to the RenderThreadImpl to clear
   // it's |video_frame_compositor_context_provider_| if it matches.
-  scoped_refptr<viz::ContextProvider> GetVideoFrameCompositorContextProvider(
-      scoped_refptr<viz::ContextProvider>);
+  scoped_refptr<viz::RasterContextProvider>
+      GetVideoFrameCompositorContextProvider(
+          scoped_refptr<viz::RasterContextProvider>);
 
   // Returns a worker context provider that will be bound on the compositor
   // thread.
@@ -676,7 +677,8 @@
 
   base::ObserverList<RenderThreadObserver>::Unchecked observers_;
 
-  scoped_refptr<viz::ContextProvider> video_frame_compositor_context_provider_;
+  scoped_refptr<viz::RasterContextProvider>
+      video_frame_compositor_context_provider_;
 
   scoped_refptr<viz::RasterContextProvider> shared_worker_context_provider_;
 
diff --git a/content/renderer/service_worker/service_worker_network_provider_for_frame.cc b/content/renderer/service_worker/service_worker_network_provider_for_frame.cc
index 357104eb..9025ebe 100644
--- a/content/renderer/service_worker/service_worker_network_provider_for_frame.cc
+++ b/content/renderer/service_worker/service_worker_network_provider_for_frame.cc
@@ -144,10 +144,6 @@
     // ServiceWorkerContainerHost since we couldn't set it up correctly due to
     // this test limitation. This way we don't crash when the associated
     // interface ptr is used.
-    //
-    // TODO(falken): Just give ServiceWorkerProviderContext a null interface ptr
-    // and make the callsites deal with it. They are supposed to anyway because
-    // OnNetworkProviderDestroyed() can reset the ptr to null at any time.
     mojo::AssociateWithDisconnectedPipe(host_info->host_request.PassHandle());
   }
   return provider;
diff --git a/content/renderer/service_worker/service_worker_provider_context.cc b/content/renderer/service_worker/service_worker_provider_context.cc
index 6f166df..f6a2649 100644
--- a/content/renderer/service_worker/service_worker_provider_context.cc
+++ b/content/renderer/service_worker/service_worker_provider_context.cc
@@ -131,6 +131,12 @@
   if (!state->subresource_loader_factory) {
     DCHECK(!state->controller_connector);
     DCHECK(state->controller_endpoint);
+
+    blink::mojom::ServiceWorkerContainerHostPtrInfo host_ptr_info =
+        CloneContainerHostPtrInfo();
+    if (!host_ptr_info)
+      return nullptr;
+
     // Create a SubresourceLoaderFactory on a background thread to avoid
     // extra contention on the main thread.
     auto task_runner = base::CreateSequencedTaskRunnerWithTraits(
@@ -138,7 +144,7 @@
     task_runner->PostTask(
         FROM_HERE,
         base::BindOnce(&CreateSubresourceLoaderFactoryForProviderContext,
-                       CloneContainerHostPtrInfo(),
+                       std::move(host_ptr_info),
                        std::move(state->controller_endpoint), state->client_id,
                        state->fallback_loader_factory->Clone(),
                        mojo::MakeRequest(&state->controller_connector),
@@ -201,6 +207,8 @@
   DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
   DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(state_for_client_);
+  if (!container_host_)
+    return nullptr;
   blink::mojom::ServiceWorkerContainerHostPtrInfo container_host_ptr_info;
   container_host_->CloneContainerHost(
       mojo::MakeRequest(&container_host_ptr_info));
@@ -214,6 +222,8 @@
 void ServiceWorkerProviderContext::PingContainerHost(
     base::OnceClosure callback) {
   DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  if (!container_host_)
+    return;
   container_host_->Ping(std::move(callback));
 }
 
@@ -232,6 +242,9 @@
     return;
   }
 
+  if (!container_host_)
+    return;
+
   container_host_->HintToUpdateServiceWorker();
 }
 
diff --git a/content/renderer/service_worker/service_worker_provider_context.h b/content/renderer/service_worker/service_worker_provider_context.h
index b41175e..a1b4951 100644
--- a/content/renderer/service_worker/service_worker_provider_context.h
+++ b/content/renderer/service_worker/service_worker_provider_context.h
@@ -136,7 +136,8 @@
 
   // S13nServiceWorker:
   // Returns a ServiceWorkerContainerHostPtrInfo to this context's container
-  // host.
+  // host. This can return null after OnNetworkProviderDestroyed() is called
+  // (in which case |this| will be destroyed soon).
   blink::mojom::ServiceWorkerContainerHostPtrInfo CloneContainerHostPtrInfo();
 
   // Called when WebServiceWorkerNetworkProvider is destructed. This function
@@ -147,6 +148,9 @@
   // ServiceWorkerProviderHost must destruct quickly in order to remove the
   // ServiceWorkerClient from the system (thus allowing unregistration/update to
   // occur and ensuring the Clients API doesn't return the client).
+  //
+  // TODO(https://crbug.com/931497): Remove this weird partially destroyed
+  // state.
   void OnNetworkProviderDestroyed();
 
   // Gets the blink::mojom::ServiceWorkerContainerHost* for sending requests to
@@ -210,7 +214,9 @@
 
   // The |container_host_| interface represents the connection to the
   // browser-side ServiceWorkerProviderHost, whose lifetime is bound to
-  // |container_host_| via the Mojo connection.
+  // |container_host_| via the Mojo connection. This may be nullptr if the Mojo
+  // connection was broken in OnNetworkProviderDestroyed().
+  //
   // The |container_host_| interface also implements functions for
   // navigator.serviceWorker, but all the methods that correspond to
   // navigator.serviceWorker.* can be used only if |this| is a provider
diff --git a/content/renderer/service_worker/service_worker_provider_context_unittest.cc b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
index 7ec251f..78bbda5d 100644
--- a/content/renderer/service_worker/service_worker_provider_context_unittest.cc
+++ b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
@@ -710,5 +711,47 @@
             *(++(client->used_features().begin())));
 }
 
+TEST_F(ServiceWorkerProviderContextTest, OnNetworkProviderDestroyed) {
+  // Make the object host for .controller.
+  auto object_host =
+      std::make_unique<MockServiceWorkerObjectHost>(200 /* version_id */);
+  blink::mojom::ServiceWorkerObjectInfoPtr object_info =
+      object_host->CreateObjectInfo();
+
+  // Make the ControllerServiceWorkerInfo.
+  FakeControllerServiceWorker fake_controller;
+  auto controller_info = blink::mojom::ControllerServiceWorkerInfo::New();
+  blink::mojom::ControllerServiceWorkerPtr controller_ptr;
+  fake_controller.Clone(mojo::MakeRequest(&controller_ptr));
+  controller_info->mode =
+      blink::mojom::ControllerServiceWorkerMode::kControlled;
+  controller_info->object_info = std::move(object_info);
+  controller_info->endpoint = controller_ptr.PassInterface();
+
+  // Make the container host and container pointers.
+  blink::mojom::ServiceWorkerContainerHostAssociatedPtr host_ptr;
+  blink::mojom::ServiceWorkerContainerHostAssociatedRequest host_request =
+      mojo::MakeRequestAssociatedWithDedicatedPipe(&host_ptr);
+  blink::mojom::ServiceWorkerContainerAssociatedPtr container_ptr;
+  blink::mojom::ServiceWorkerContainerAssociatedRequest container_request =
+      mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr);
+
+  // Make the provider context.
+  auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
+      11 /* provider_id */, blink::mojom::ServiceWorkerProviderType::kForWindow,
+      std::move(container_request), host_ptr.PassInterface(),
+      std::move(controller_info), loader_factory_);
+
+  // Put it in the weird state to test.
+  provider_context->OnNetworkProviderDestroyed();
+
+  // Calling these in the weird state shouldn't crash.
+  EXPECT_FALSE(provider_context->container_host());
+  EXPECT_FALSE(provider_context->CloneContainerHostPtrInfo());
+  provider_context->PingContainerHost(base::DoNothing());
+  provider_context->DispatchNetworkQuiet();
+  provider_context->NotifyExecutionReady();
+}
+
 }  // namespace service_worker_provider_context_unittest
 }  // namespace content
diff --git a/content/renderer/worker/service_worker_network_provider_for_worker.cc b/content/renderer/worker/service_worker_network_provider_for_worker.cc
index 8e4fe41..1106d554 100644
--- a/content/renderer/worker/service_worker_network_provider_for_worker.cc
+++ b/content/renderer/worker/service_worker_network_provider_for_worker.cc
@@ -68,11 +68,6 @@
       // ServiceWorkerContainerHost since we couldn't set it up correctly due
       // to this test limitation. This way we don't crash when the associated
       // interface ptr is used.
-      //
-      // TODO(falken): Just give ServiceWorkerProviderContext a null interface
-      // ptr and make the callsites deal with it. They are supposed to anyway
-      // because OnNetworkProviderDestroyed() can reset the ptr to null at any
-      // time.
       mojo::AssociateWithDisconnectedPipe(host_info->host_request.PassHandle());
     }
   }
diff --git a/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc b/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc
index 524e7ae..59dd927 100644
--- a/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc
+++ b/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc
@@ -25,8 +25,13 @@
 
 namespace {
 
+namespace dnr_api = api::declarative_net_request;
+
 // Returns true if the given |extension| has a registered ruleset. If it
 // doesn't, returns false and populates |error|.
+// TODO(crbug.com/931967): Using HasRegisteredRuleset for PreRunValidation means
+// that the extension function will fail if the ruleset for the extension is
+// currently being indexed. Fix this.
 bool HasRegisteredRuleset(content::BrowserContext* context,
                           const ExtensionId& extension_id,
                           std::string* error) {
@@ -87,11 +92,10 @@
       break;
   }
 
-  if (static_cast<int>(new_set.size()) >
-      api::declarative_net_request::MAX_NUMBER_OF_ALLOWED_PAGES) {
+  if (static_cast<int>(new_set.size()) > dnr_api::MAX_NUMBER_OF_ALLOWED_PAGES) {
     return RespondNow(Error(base::StringPrintf(
         "The number of allowed page patterns can't exceed %d",
-        api::declarative_net_request::MAX_NUMBER_OF_ALLOWED_PAGES)));
+        dnr_api::MAX_NUMBER_OF_ALLOWED_PAGES)));
   }
 
   // Persist |new_set| as part of preferences.
@@ -130,7 +134,7 @@
 
 ExtensionFunction::ResponseAction
 DeclarativeNetRequestAddAllowedPagesFunction::Run() {
-  using Params = api::declarative_net_request::AddAllowedPages::Params;
+  using Params = dnr_api::AddAllowedPages::Params;
 
   base::string16 error;
   std::unique_ptr<Params> params(Params::Create(*args_, &error));
@@ -149,7 +153,7 @@
 
 ExtensionFunction::ResponseAction
 DeclarativeNetRequestRemoveAllowedPagesFunction::Run() {
-  using Params = api::declarative_net_request::AddAllowedPages::Params;
+  using Params = dnr_api::AddAllowedPages::Params;
 
   base::string16 error;
   std::unique_ptr<Params> params(Params::Create(*args_, &error));
@@ -177,9 +181,59 @@
   const ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context());
   URLPatternSet current_set = prefs->GetDNRAllowedPages(extension_id());
 
-  return RespondNow(ArgumentList(
-      api::declarative_net_request::GetAllowedPages::Results::Create(
-          *current_set.ToStringVector())));
+  return RespondNow(ArgumentList(dnr_api::GetAllowedPages::Results::Create(
+      *current_set.ToStringVector())));
+}
+
+DeclarativeNetRequestAddDynamicRulesFunction::
+    DeclarativeNetRequestAddDynamicRulesFunction() = default;
+DeclarativeNetRequestAddDynamicRulesFunction::
+    ~DeclarativeNetRequestAddDynamicRulesFunction() = default;
+
+bool DeclarativeNetRequestAddDynamicRulesFunction::PreRunValidation(
+    std::string* error) {
+  return UIThreadExtensionFunction::PreRunValidation(error) &&
+         HasRegisteredRuleset(browser_context(), extension_id(), error);
+}
+
+ExtensionFunction::ResponseAction
+DeclarativeNetRequestAddDynamicRulesFunction::Run() {
+  // TODO(crbug.com/930961): Implement this.
+  return RespondNow(Error("Not implemented"));
+}
+
+DeclarativeNetRequestRemoveDynamicRulesFunction::
+    DeclarativeNetRequestRemoveDynamicRulesFunction() = default;
+DeclarativeNetRequestRemoveDynamicRulesFunction::
+    ~DeclarativeNetRequestRemoveDynamicRulesFunction() = default;
+
+bool DeclarativeNetRequestRemoveDynamicRulesFunction::PreRunValidation(
+    std::string* error) {
+  return UIThreadExtensionFunction::PreRunValidation(error) &&
+         HasRegisteredRuleset(browser_context(), extension_id(), error);
+}
+
+ExtensionFunction::ResponseAction
+DeclarativeNetRequestRemoveDynamicRulesFunction::Run() {
+  // TODO(crbug.com/930961): Implement this.
+  return RespondNow(Error("Not implemented"));
+}
+
+DeclarativeNetRequestGetDynamicRulesFunction::
+    DeclarativeNetRequestGetDynamicRulesFunction() = default;
+DeclarativeNetRequestGetDynamicRulesFunction::
+    ~DeclarativeNetRequestGetDynamicRulesFunction() = default;
+
+bool DeclarativeNetRequestGetDynamicRulesFunction::PreRunValidation(
+    std::string* error) {
+  return UIThreadExtensionFunction::PreRunValidation(error) &&
+         HasRegisteredRuleset(browser_context(), extension_id(), error);
+}
+
+ExtensionFunction::ResponseAction
+DeclarativeNetRequestGetDynamicRulesFunction::Run() {
+  // TODO(crbug.com/930961): Implement this.
+  return RespondNow(Error("Not implemented"));
 }
 
 }  // namespace extensions
diff --git a/extensions/browser/api/declarative_net_request/declarative_net_request_api.h b/extensions/browser/api/declarative_net_request/declarative_net_request_api.h
index 67f28531..b698069 100644
--- a/extensions/browser/api/declarative_net_request/declarative_net_request_api.h
+++ b/extensions/browser/api/declarative_net_request/declarative_net_request_api.h
@@ -96,6 +96,60 @@
   DISALLOW_COPY_AND_ASSIGN(DeclarativeNetRequestGetAllowedPagesFunction);
 };
 
+class DeclarativeNetRequestAddDynamicRulesFunction
+    : public UIThreadExtensionFunction {
+ public:
+  DeclarativeNetRequestAddDynamicRulesFunction();
+  DECLARE_EXTENSION_FUNCTION("declarativeNetRequest.addDynamicRules",
+                             DECLARATIVENETREQUEST_ADDDYNAMICRULES);
+
+ protected:
+  ~DeclarativeNetRequestAddDynamicRulesFunction() override;
+
+  // ExtensionFunction override:
+  bool PreRunValidation(std::string* error) override;
+  ExtensionFunction::ResponseAction Run() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DeclarativeNetRequestAddDynamicRulesFunction);
+};
+
+class DeclarativeNetRequestRemoveDynamicRulesFunction
+    : public UIThreadExtensionFunction {
+ public:
+  DeclarativeNetRequestRemoveDynamicRulesFunction();
+  DECLARE_EXTENSION_FUNCTION("declarativeNetRequest.removeDynamicRules",
+                             DECLARATIVENETREQUEST_REMOVEDYNAMICRULES);
+
+ protected:
+  ~DeclarativeNetRequestRemoveDynamicRulesFunction() override;
+
+  // ExtensionFunction override:
+  bool PreRunValidation(std::string* error) override;
+  ExtensionFunction::ResponseAction Run() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DeclarativeNetRequestRemoveDynamicRulesFunction);
+};
+
+class DeclarativeNetRequestGetDynamicRulesFunction
+    : public UIThreadExtensionFunction {
+ public:
+  DeclarativeNetRequestGetDynamicRulesFunction();
+  DECLARE_EXTENSION_FUNCTION("declarativeNetRequest.getDynamicRules",
+                             DECLARATIVENETREQUEST_GETDYNAMICRULES);
+
+ protected:
+  ~DeclarativeNetRequestGetDynamicRulesFunction() override;
+
+  // ExtensionFunction override:
+  bool PreRunValidation(std::string* error) override;
+  ExtensionFunction::ResponseAction Run() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DeclarativeNetRequestGetDynamicRulesFunction);
+};
+
 }  // namespace extensions
 
 #endif  // EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_DECLARATIVE_NET_REQUEST_API_H_
diff --git a/extensions/browser/api/declarative_net_request/indexed_rule.cc b/extensions/browser/api/declarative_net_request/indexed_rule.cc
index b33543c..cfdc48a 100644
--- a/extensions/browser/api/declarative_net_request/indexed_rule.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_rule.cc
@@ -263,74 +263,73 @@
 IndexedRule& IndexedRule::operator=(IndexedRule&& other) = default;
 
 // static
-ParseResult IndexedRule::CreateIndexedRule(
-    std::unique_ptr<dnr_api::Rule> parsed_rule,
-    IndexedRule* indexed_rule) {
+ParseResult IndexedRule::CreateIndexedRule(dnr_api::Rule parsed_rule,
+                                           IndexedRule* indexed_rule) {
   DCHECK(indexed_rule);
   DCHECK(IsAPIAvailable());
 
-  if (parsed_rule->id < kMinValidID)
+  if (parsed_rule.id < kMinValidID)
     return ParseResult::ERROR_INVALID_RULE_ID;
 
   const bool is_redirect_rule =
-      parsed_rule->action.type == dnr_api::RULE_ACTION_TYPE_REDIRECT;
+      parsed_rule.action.type == dnr_api::RULE_ACTION_TYPE_REDIRECT;
   if (is_redirect_rule) {
-    if (!parsed_rule->action.redirect_url ||
-        parsed_rule->action.redirect_url->empty()) {
+    if (!parsed_rule.action.redirect_url ||
+        parsed_rule.action.redirect_url->empty()) {
       return ParseResult::ERROR_EMPTY_REDIRECT_URL;
     }
-    if (!GURL(*parsed_rule->action.redirect_url).is_valid())
+    if (!GURL(*parsed_rule.action.redirect_url).is_valid())
       return ParseResult::ERROR_INVALID_REDIRECT_URL;
-    if (!parsed_rule->priority)
+    if (!parsed_rule.priority)
       return ParseResult::ERROR_EMPTY_REDIRECT_RULE_PRIORITY;
-    if (*parsed_rule->priority < kMinValidPriority)
+    if (*parsed_rule.priority < kMinValidPriority)
       return ParseResult::ERROR_INVALID_REDIRECT_RULE_PRIORITY;
   }
 
-  if (parsed_rule->condition.domains && parsed_rule->condition.domains->empty())
+  if (parsed_rule.condition.domains && parsed_rule.condition.domains->empty())
     return ParseResult::ERROR_EMPTY_DOMAINS_LIST;
 
-  if (parsed_rule->condition.resource_types &&
-      parsed_rule->condition.resource_types->empty()) {
+  if (parsed_rule.condition.resource_types &&
+      parsed_rule.condition.resource_types->empty()) {
     return ParseResult::ERROR_EMPTY_RESOURCE_TYPES_LIST;
   }
 
-  if (parsed_rule->condition.url_filter) {
-    if (parsed_rule->condition.url_filter->empty())
+  if (parsed_rule.condition.url_filter) {
+    if (parsed_rule.condition.url_filter->empty())
       return ParseResult::ERROR_EMPTY_URL_FILTER;
-    if (!base::IsStringASCII(*parsed_rule->condition.url_filter))
+    if (!base::IsStringASCII(*parsed_rule.condition.url_filter))
       return ParseResult::ERROR_NON_ASCII_URL_FILTER;
   }
 
-  indexed_rule->id = base::checked_cast<uint32_t>(parsed_rule->id);
+  indexed_rule->id = base::checked_cast<uint32_t>(parsed_rule.id);
   indexed_rule->priority = base::checked_cast<uint32_t>(
-      is_redirect_rule ? *parsed_rule->priority : kDefaultPriority);
-  indexed_rule->options = GetOptionsMask(*parsed_rule);
-  indexed_rule->activation_types = GetActivationTypes(*parsed_rule);
+      is_redirect_rule ? *parsed_rule.priority : kDefaultPriority);
+  indexed_rule->options = GetOptionsMask(parsed_rule);
+  indexed_rule->activation_types = GetActivationTypes(parsed_rule);
 
   {
-    ParseResult result = ComputeElementTypes(parsed_rule->condition,
+    ParseResult result = ComputeElementTypes(parsed_rule.condition,
                                              &indexed_rule->element_types);
     if (result != ParseResult::SUCCESS)
       return result;
   }
 
-  if (!CanonicalizeDomains(std::move(parsed_rule->condition.domains),
+  if (!CanonicalizeDomains(std::move(parsed_rule.condition.domains),
                            &indexed_rule->domains)) {
     return ParseResult::ERROR_NON_ASCII_DOMAIN;
   }
 
-  if (!CanonicalizeDomains(std::move(parsed_rule->condition.excluded_domains),
+  if (!CanonicalizeDomains(std::move(parsed_rule.condition.excluded_domains),
                            &indexed_rule->excluded_domains)) {
     return ParseResult::ERROR_NON_ASCII_EXCLUDED_DOMAIN;
   }
 
   if (is_redirect_rule)
-    indexed_rule->redirect_url = std::move(*parsed_rule->action.redirect_url);
+    indexed_rule->redirect_url = std::move(*parsed_rule.action.redirect_url);
 
   // Parse the |anchor_left|, |anchor_right|, |url_pattern_type| and
   // |url_pattern| fields.
-  UrlFilterParser::Parse(std::move(parsed_rule->condition.url_filter),
+  UrlFilterParser::Parse(std::move(parsed_rule.condition.url_filter),
                          indexed_rule);
 
   // Lower-case case-insensitive patterns as required by url pattern index.
diff --git a/extensions/browser/api/declarative_net_request/indexed_rule.h b/extensions/browser/api/declarative_net_request/indexed_rule.h
index 6240322..710d415 100644
--- a/extensions/browser/api/declarative_net_request/indexed_rule.h
+++ b/extensions/browser/api/declarative_net_request/indexed_rule.h
@@ -6,7 +6,6 @@
 #define EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_INDEXED_RULE_H_
 
 #include <stdint.h>
-#include <memory>
 #include <string>
 #include <vector>
 
@@ -35,8 +34,7 @@
   IndexedRule& operator=(IndexedRule&& other);
 
   static ParseResult CreateIndexedRule(
-      std::unique_ptr<extensions::api::declarative_net_request::Rule>
-          parsed_rule,
+      extensions::api::declarative_net_request::Rule parsed_rule,
       IndexedRule* indexed_rule);
 
   // These fields correspond to the attributes of a flatbuffer UrlRule, as
diff --git a/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc b/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc
index c36dc61..fbf780f 100644
--- a/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc
@@ -26,11 +26,11 @@
 namespace flat_rule = url_pattern_index::flat;
 namespace dnr_api = extensions::api::declarative_net_request;
 
-std::unique_ptr<dnr_api::Rule> CreateGenericParsedRule() {
-  auto rule = std::make_unique<dnr_api::Rule>();
-  rule->id = kMinValidID;
-  rule->condition.url_filter = std::make_unique<std::string>("filter");
-  rule->action.type = dnr_api::RULE_ACTION_TYPE_BLOCK;
+dnr_api::Rule CreateGenericParsedRule() {
+  dnr_api::Rule rule;
+  rule.id = kMinValidID;
+  rule.condition.url_filter = std::make_unique<std::string>("filter");
+  rule.action.type = dnr_api::RULE_ACTION_TYPE_BLOCK;
   return rule;
 }
 
@@ -55,8 +55,8 @@
   };
   for (size_t i = 0; i < base::size(cases); ++i) {
     SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i));
-    std::unique_ptr<dnr_api::Rule> rule = CreateGenericParsedRule();
-    rule->id = cases[i].id;
+    dnr_api::Rule rule = CreateGenericParsedRule();
+    rule.id = cases[i].id;
 
     IndexedRule indexed_rule;
     ParseResult result =
@@ -87,10 +87,10 @@
 
   for (size_t i = 0; i < base::size(cases); ++i) {
     SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i));
-    std::unique_ptr<dnr_api::Rule> rule = CreateGenericParsedRule();
-    rule->priority = std::move(cases[i].priority);
-    rule->action.type = dnr_api::RULE_ACTION_TYPE_REDIRECT;
-    rule->action.redirect_url =
+    dnr_api::Rule rule = CreateGenericParsedRule();
+    rule.priority = std::move(cases[i].priority);
+    rule.action.type = dnr_api::RULE_ACTION_TYPE_REDIRECT;
+    rule.action.redirect_url =
         std::make_unique<std::string>("http://google.com");
 
     IndexedRule indexed_rule;
@@ -104,8 +104,8 @@
 
   // Ensure priority is ignored for non-redirect rules.
   {
-    std::unique_ptr<dnr_api::Rule> rule = CreateGenericParsedRule();
-    rule->priority = std::make_unique<int>(5);
+    dnr_api::Rule rule = CreateGenericParsedRule();
+    rule.priority = std::make_unique<int>(5);
     IndexedRule indexed_rule;
     ParseResult result =
         IndexedRule::CreateIndexedRule(std::move(rule), &indexed_rule);
@@ -137,10 +137,10 @@
 
   for (size_t i = 0; i < base::size(cases); ++i) {
     SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i));
-    std::unique_ptr<dnr_api::Rule> rule = CreateGenericParsedRule();
-    rule->condition.domain_type = cases[i].domain_type;
-    rule->action.type = cases[i].action_type;
-    rule->condition.is_url_filter_case_sensitive =
+    dnr_api::Rule rule = CreateGenericParsedRule();
+    rule.condition.domain_type = cases[i].domain_type;
+    rule.action.type = cases[i].action_type;
+    rule.condition.is_url_filter_case_sensitive =
         std::move(cases[i].is_url_filter_case_sensitive);
 
     IndexedRule indexed_rule;
@@ -202,9 +202,9 @@
 
   for (size_t i = 0; i < base::size(cases); ++i) {
     SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i));
-    std::unique_ptr<dnr_api::Rule> rule = CreateGenericParsedRule();
-    rule->condition.resource_types = std::move(cases[i].resource_types);
-    rule->condition.excluded_resource_types =
+    dnr_api::Rule rule = CreateGenericParsedRule();
+    rule.condition.resource_types = std::move(cases[i].resource_types);
+    rule.condition.excluded_resource_types =
         std::move(cases[i].excluded_resource_types);
 
     IndexedRule indexed_rule;
@@ -273,8 +273,8 @@
 
   for (size_t i = 0; i < base::size(cases); ++i) {
     SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i));
-    std::unique_ptr<dnr_api::Rule> rule = CreateGenericParsedRule();
-    rule->condition.url_filter = std::move(cases[i].input_url_filter);
+    dnr_api::Rule rule = CreateGenericParsedRule();
+    rule.condition.url_filter = std::move(cases[i].input_url_filter);
 
     IndexedRule indexed_rule;
     ParseResult result =
@@ -305,9 +305,9 @@
   };
 
   for (auto& test_case : test_cases) {
-    std::unique_ptr<dnr_api::Rule> rule = CreateGenericParsedRule();
-    rule->condition.url_filter = std::make_unique<std::string>(kPattern);
-    rule->condition.is_url_filter_case_sensitive =
+    dnr_api::Rule rule = CreateGenericParsedRule();
+    rule.condition.url_filter = std::make_unique<std::string>(kPattern);
+    rule.condition.is_url_filter_case_sensitive =
         std::move(test_case.is_url_filter_case_sensitive);
     IndexedRule indexed_rule;
     ASSERT_EQ(ParseResult::SUCCESS,
@@ -369,9 +369,9 @@
 
   for (size_t i = 0; i < base::size(cases); ++i) {
     SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i));
-    std::unique_ptr<dnr_api::Rule> rule = CreateGenericParsedRule();
-    rule->condition.domains = std::move(cases[i].domains);
-    rule->condition.excluded_domains = std::move(cases[i].excluded_domains);
+    dnr_api::Rule rule = CreateGenericParsedRule();
+    rule.condition.domains = std::move(cases[i].domains);
+    rule.condition.excluded_domains = std::move(cases[i].excluded_domains);
 
     IndexedRule indexed_rule;
     ParseResult result =
@@ -402,10 +402,10 @@
 
   for (size_t i = 0; i < base::size(cases); ++i) {
     SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i));
-    std::unique_ptr<dnr_api::Rule> rule = CreateGenericParsedRule();
-    rule->action.redirect_url = std::move(cases[i].redirect_url);
-    rule->action.type = dnr_api::RULE_ACTION_TYPE_REDIRECT;
-    rule->priority = std::make_unique<int>(kMinValidPriority);
+    dnr_api::Rule rule = CreateGenericParsedRule();
+    rule.action.redirect_url = std::move(cases[i].redirect_url);
+    rule.action.type = dnr_api::RULE_ACTION_TYPE_REDIRECT;
+    rule.priority = std::make_unique<int>(kMinValidPriority);
 
     IndexedRule indexed_rule;
     ParseResult result =
diff --git a/extensions/browser/api/declarative_net_request/utils.cc b/extensions/browser/api/declarative_net_request/utils.cc
index 9b2fa30b..599eba3 100644
--- a/extensions/browser/api/declarative_net_request/utils.cc
+++ b/extensions/browser/api/declarative_net_request/utils.cc
@@ -171,17 +171,17 @@
   base::ElapsedTimer timer;
   {
     std::set<int> id_set;  // Ensure all ids are distinct.
-    std::unique_ptr<dnr_api::Rule> parsed_rule;
 
     const auto& rules_list = rules.GetList();
     for (size_t i = 0; i < rules_list.size(); i++) {
+      dnr_api::Rule parsed_rule;
       base::string16 parse_error;
-      parsed_rule = dnr_api::Rule::FromValue(rules_list[i], &parse_error);
 
       // Ignore rules which can't be successfully parsed and show an install
       // warning for them. A hard error is not thrown to maintain backwards
       // compatibility.
-      if (!parsed_rule || !parse_error.empty()) {
+      if (!dnr_api::Rule::Populate(rules_list[i], &parsed_rule, &parse_error) ||
+          !parse_error.empty()) {
         if (unparsed_warning_count < kMaxUnparsedRulesWarnings) {
           ++unparsed_warning_count;
           std::string rule_location;
@@ -205,7 +205,7 @@
         continue;
       }
 
-      int rule_id = parsed_rule->id;
+      int rule_id = parsed_rule.id;
       bool inserted = id_set.insert(rule_id).second;
       if (!inserted)
         return ParseInfo(ParseResult::ERROR_DUPLICATE_IDS, rule_id);
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 376ee04..ab529fc 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1378,6 +1378,9 @@
   PASSWORDSPRIVATE_CHANGESAVEDPASSWORD = 1315,
   AUTOTESTPRIVATE_SETWHITELISTEDPREF = 1316,
   SAFEBROWSINGPRIVATE_GETREFERRERCHAIN = 1317,
+  DECLARATIVENETREQUEST_ADDDYNAMICRULES = 1318,
+  DECLARATIVENETREQUEST_REMOVEDYNAMICRULES = 1319,
+  DECLARATIVENETREQUEST_GETDYNAMICRULES = 1320,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index 63a47d9..b0b1f04 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -126,6 +126,21 @@
     "dependencies": ["permission:declarativeNetRequest"],
     "contexts": ["blessed_extension"]
   },
+  "declarativeNetRequest.addDynamicRules": {
+    "dependencies": ["permission:declarativeNetRequest"],
+    "contexts": ["blessed_extension"],
+    "channel": "trunk"
+  },
+  "declarativeNetRequest.removeDynamicRules": {
+    "dependencies": ["permission:declarativeNetRequest"],
+    "contexts": ["blessed_extension"],
+    "channel": "trunk"
+  },
+  "declarativeNetRequest.getDynamicRules": {
+    "dependencies": ["permission:declarativeNetRequest"],
+    "contexts": ["blessed_extension"],
+    "channel": "trunk"
+  },
   "declarativeWebRequest": {
     "dependencies": ["permission:declarativeWebRequest"],
     "contexts": ["blessed_extension"]
diff --git a/extensions/common/api/declarative_net_request.idl b/extensions/common/api/declarative_net_request.idl
index 337c7ad2..de1c1a1 100644
--- a/extensions/common/api/declarative_net_request.idl
+++ b/extensions/common/api/declarative_net_request.idl
@@ -137,8 +137,16 @@
 
   callback EmptyCallback = void();
   callback GetAllowedPagesCallback = void(DOMString[] result);
+  callback GetRulesCallback = void(Rule[] rules);
 
   interface Functions {
+
+    // TODO(crbug.com/930961): Enable documentation for these functions once
+    // they are implemented.
+    [nodoc] static void addDynamicRules(Rule[] rules, optional EmptyCallback callback);
+    [nodoc] static void removeDynamicRules(long[] rule_ids, optional EmptyCallback callback);
+    [nodoc] static void getDynamicRules(GetRulesCallback callback);
+
     // Adds <code>page_patterns</code> to the set of allowed pages. Requests
     // from these pages are not intercepted by the extension. These are
     // persisted across browser sessions.
diff --git a/extensions/renderer/ipc_message_sender.cc b/extensions/renderer/ipc_message_sender.cc
index a641db3..2a0357a 100644
--- a/extensions/renderer/ipc_message_sender.cc
+++ b/extensions/renderer/ipc_message_sender.cc
@@ -176,6 +176,8 @@
       }
       case MessageTarget::TAB: {
         DCHECK(extension);
+        DCHECK_NE(script_context->context_type(),
+                  Feature::CONTENT_SCRIPT_CONTEXT);
         ExtensionMsg_TabTargetConnectionInfo info;
         info.tab_id = *target.tab_id;
         info.frame_id = *target.frame_id;
diff --git a/extensions/renderer/messaging_bindings.cc b/extensions/renderer/messaging_bindings.cc
index 1a38ac07..7bfe76a 100644
--- a/extensions/renderer/messaging_bindings.cc
+++ b/extensions/renderer/messaging_bindings.cc
@@ -254,6 +254,8 @@
   if (!render_frame)
     return;
 
+  DCHECK_NE(context()->context_type(), Feature::CONTENT_SCRIPT_CONTEXT);
+
   // tabs_custom_bindings.js unwraps arguments to tabs.connect/sendMessage and
   // passes them to OpenChannelToTab, in the following order:
   // - |tab_id| - Positive number that specifies the destination of the channel.
diff --git a/google_apis/gaia/oauth2_access_token_fetcher_impl_unittest.cc b/google_apis/gaia/oauth2_access_token_fetcher_impl_unittest.cc
index 0acdc97..848ce5e 100644
--- a/google_apis/gaia/oauth2_access_token_fetcher_impl_unittest.cc
+++ b/google_apis/gaia/oauth2_access_token_fetcher_impl_unittest.cc
@@ -74,20 +74,15 @@
 class OAuth2AccessTokenFetcherImplTest : public testing::Test {
  public:
   OAuth2AccessTokenFetcherImplTest()
-      : shared_url_loader_factory_(
-            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-                &url_loader_factory_)),
-        fetcher_(&consumer_, shared_url_loader_factory_, "refresh_token") {
+      : fetcher_(&consumer_,
+                 url_loader_factory_.GetSafeWeakWrapper(),
+                 "refresh_token") {
     url_loader_factory_.SetInterceptor(base::BindRepeating(
         &URLLoaderFactoryInterceptor::Intercept,
         base::Unretained(&url_loader_factory_interceptor_)));
     base::RunLoop().RunUntilIdle();
   }
 
-  ~OAuth2AccessTokenFetcherImplTest() override {
-    shared_url_loader_factory_->Detach();
-  }
-
   void SetupGetAccessToken(int net_error_code,
                            net::HttpStatusCode http_response_code,
                            const std::string& body) {
@@ -124,8 +119,6 @@
   MockOAuth2AccessTokenConsumer consumer_;
   URLLoaderFactoryInterceptor url_loader_factory_interceptor_;
   network::TestURLLoaderFactory url_loader_factory_;
-  scoped_refptr<network::WeakWrapperSharedURLLoaderFactory>
-      shared_url_loader_factory_;
   OAuth2AccessTokenFetcherImpl fetcher_;
 };
 
diff --git a/ios/build/bots/chromium.fyi/ios12-sdk-simulator.json b/ios/build/bots/chromium.fyi/ios12-sdk-simulator.json
index 017274ec..b3f0031 100644
--- a/ios/build/bots/chromium.fyi/ios12-sdk-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios12-sdk-simulator.json
@@ -29,7 +29,8 @@
       "os": "12.1",
       "host os": "Mac-10.13.6",
       "pool": "Chrome",
-      "shards": 4
+      "shards": 4,
+      "priority": 35
     },
     {
       "xcode parallelization": true,
@@ -37,7 +38,8 @@
       "device type": "iPhone X",
       "os": "12.1",
       "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "pool": "Chrome",
+      "priority": 35
     },
     {
       "xcode parallelization": true,
@@ -46,7 +48,8 @@
       "os": "12.1",
       "host os": "Mac-10.13.6",
       "pool": "Chrome",
-      "shards": 4
+      "shards": 4,
+      "priority": 35
     },
     {
       "xcode parallelization": true,
@@ -54,7 +57,8 @@
       "device type": "iPad Air 2",
       "os": "12.1",
       "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "pool": "Chrome",
+      "priority": 35
     },
     {
       "xcode parallelization": true,
@@ -63,7 +67,8 @@
       "os": "12.1",
       "host os": "Mac-10.13.6",
       "pool": "Chrome",
-      "shards": 4
+      "shards": 4,
+      "priority": 35
     },
     {
       "xcode parallelization": true,
@@ -71,7 +76,8 @@
       "device type": "iPhone 7",
       "os": "12.1",
       "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "pool": "Chrome",
+      "priority": 35
     },
     {
       "xcode parallelization": true,
@@ -80,7 +86,8 @@
       "os": "11.4",
       "host os": "Mac-10.13.6",
       "pool": "Chrome",
-      "shards": 4
+      "shards": 4,
+      "priority": 35
     },
     {
       "xcode parallelization": true,
@@ -88,7 +95,8 @@
       "device type": "iPhone 7",
       "os": "11.4",
       "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "pool": "Chrome",
+      "priority": 35
     },
     {
       "xcode parallelization": true,
@@ -97,7 +105,8 @@
       "os": "11.4",
       "host os": "Mac-10.13.6",
       "pool": "Chrome",
-      "shards": 4
+      "shards": 4,
+      "priority": 35
     },
     {
       "xcode parallelization": true,
@@ -105,7 +114,8 @@
       "device type": "iPad Air 2",
       "os": "11.4",
       "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "pool": "Chrome",
+      "priority": 35
     },
     {
       "xcode parallelization": true,
@@ -114,7 +124,8 @@
       "os": "11.4",
       "host os": "Mac-10.13.6",
       "pool": "Chrome",
-      "shards": 4
+      "shards": 4,
+      "priority": 35
     },
     {
       "xcode parallelization": true,
@@ -122,7 +133,8 @@
       "device type": "iPhone X",
       "os": "11.4",
       "host os": "Mac-10.13.6",
-      "pool": "Chrome"
+      "pool": "Chrome",
+      "priority": 35
     }
   ]
 }
diff --git a/ios/build/bots/scripts/test_runner.py b/ios/build/bots/scripts/test_runner.py
index 51f3ffa8..5ea5823 100644
--- a/ios/build/bots/scripts/test_runner.py
+++ b/ios/build/bots/scripts/test_runner.py
@@ -837,15 +837,22 @@
 
   def tear_down(self):
     """Performs cleanup actions which must occur after every test launch."""
+    LOGGER.debug('Extracting test data.')
     self.extract_test_data()
+    LOGGER.debug('Retrieving crash reports.')
     self.retrieve_crash_reports()
+    LOGGER.debug('Retrieving derived data.')
     self.retrieve_derived_data()
+    LOGGER.debug('Making desktop screenshots.')
     self.screenshot_desktop()
+    LOGGER.debug('Killing simulators.')
     self.kill_simulators()
+    LOGGER.debug('Wiping simulator.')
     self.wipe_simulator()
     if os.path.exists(self.homedir):
       shutil.rmtree(self.homedir, ignore_errors=True)
       self.homedir = ''
+    LOGGER.debug('End of tear_down.')
 
   def run_tests(self, test_shard=None):
     """Runs passed-in tests. Builds a command and create a simulator to
diff --git a/ios/build/bots/scripts/xcodebuild_runner.py b/ios/build/bots/scripts/xcodebuild_runner.py
index c4ca40c7..6e4760be 100644
--- a/ios/build/bots/scripts/xcodebuild_runner.py
+++ b/ios/build/bots/scripts/xcodebuild_runner.py
@@ -8,16 +8,21 @@
 
 import collections
 import json
+import logging
 import multiprocessing
 import os
 import plistlib
 import re
 import shutil
 import subprocess
+import threading
 import time
 
 import test_runner
 
+LOGGER = logging.getLogger(__name__)
+READLINE_TIMEOUT = 300
+
 
 class LaunchCommandCreationError(test_runner.TestRunnerError):
   """One of launch command parameters was not set properly."""
@@ -33,6 +38,20 @@
     super(LaunchCommandPoolCreationError, self).__init__(message)
 
 
+def terminate_process(proc):
+  """Terminates the process.
+
+  If an error occurs ignore it, just print out a message.
+
+  Args:
+    proc: A subprocess.
+  """
+  try:
+    proc.terminate()
+  except OSError as ex:
+    print 'Error while killing a process: %s' % ex
+
+
 def test_status_summary(summary_plist):
   """Gets status summary from TestSummaries.plist.
 
@@ -355,8 +374,18 @@
         stdout=subprocess.PIPE,
         stderr=subprocess.STDOUT,
     )
+
     while True:
+      # It seems that subprocess.stdout.readline() is stuck from time to time
+      # and tests fail because of TIMEOUT.
+      # Try to fix the issue by adding timer-thread for 5 mins
+      # that will kill `frozen` running process if no new line is read
+      # and will finish test attempt.
+      # If new line appears in 5 mins, just cancel timer.
+      timer = threading.Timer(READLINE_TIMEOUT, terminate_process, [proc])
+      timer.start()
       line = proc.stdout.readline()
+      timer.cancel()
       if not line:
         break
       line = line.rstrip()
@@ -598,8 +627,8 @@
       self.logs[' '.join(result['cmd'])] = result['test_results']
       self.test_results['commands'].append(
           {'cmd': ' '.join(result['cmd']), 'logs': result['logs']})
-
     self.test_results['end_run'] = int(time.time())
+    LOGGER.debug('Test ended.')
     # Test is failed if there are failures for the last run.
     return not self.test_results['commands'][-1]['logs']['failed']
 
diff --git a/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc b/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc
index 0848537..f7f99ac 100644
--- a/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc
+++ b/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
+#include "components/signin/core/browser/test_signin_client.h"
 #include "components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/signin/account_fetcher_service_factory.h"
@@ -71,9 +72,16 @@
   return account_fetcher_service;
 }
 
+std::unique_ptr<KeyedService> BuildTestSigninClient(web::BrowserState* state) {
+  return std::make_unique<TestSigninClient>(
+      ios::ChromeBrowserState::FromBrowserState(state)->GetPrefs());
+}
+
 TestChromeBrowserState::TestingFactories GetIdentityTestEnvironmentFactories(
     bool use_ios_token_service_delegate) {
-  return {{ios::AccountFetcherServiceFactory::GetInstance(),
+  return {{SigninClientFactory::GetInstance(),
+           base::BindRepeating(&BuildTestSigninClient)},
+          {ios::AccountFetcherServiceFactory::GetInstance(),
            base::BindRepeating(&BuildFakeAccountFetcherService)},
           {ProfileOAuth2TokenServiceFactory::GetInstance(),
            base::BindRepeating(use_ios_token_service_delegate
diff --git a/ios/chrome/browser/sync/profile_sync_service_factory.cc b/ios/chrome/browser/sync/profile_sync_service_factory.cc
index 9bb22a1..6e3c021 100644
--- a/ios/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/ios/chrome/browser/sync/profile_sync_service_factory.cc
@@ -10,6 +10,7 @@
 #include "base/no_destructor.h"
 #include "base/task/post_task.h"
 #include "base/time/time.h"
+#include "components/browser_sync/browser_sync_switches.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/invalidation/impl/invalidation_switches.h"
 #include "components/invalidation/impl/profile_invalidation_provider.h"
@@ -76,7 +77,7 @@
 // static
 syncer::SyncService* ProfileSyncServiceFactory::GetForBrowserState(
     ios::ChromeBrowserState* browser_state) {
-  if (!browser_sync::ProfileSyncService::IsSyncAllowedByFlag())
+  if (!switches::IsSyncAllowedByFlag())
     return nullptr;
 
   return static_cast<syncer::SyncService*>(
@@ -86,7 +87,7 @@
 // static
 syncer::SyncService* ProfileSyncServiceFactory::GetForBrowserStateIfExists(
     ios::ChromeBrowserState* browser_state) {
-  if (!browser_sync::ProfileSyncService::IsSyncAllowedByFlag())
+  if (!switches::IsSyncAllowedByFlag())
     return nullptr;
 
   return static_cast<syncer::SyncService*>(
diff --git a/ios/chrome/browser/ui/settings/sync/sync_encryption_table_view_controller.mm b/ios/chrome/browser/ui/settings/sync/sync_encryption_table_view_controller.mm
index 5558326..57cacfdd 100644
--- a/ios/chrome/browser/ui/settings/sync/sync_encryption_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/sync/sync_encryption_table_view_controller.mm
@@ -8,7 +8,7 @@
 
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
-#include "components/browser_sync/profile_sync_service.h"
+#include "components/browser_sync/browser_sync_switches.h"
 #include "components/google/core/common/google_util.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/sync/base/sync_prefs.h"
@@ -109,7 +109,7 @@
 
 // Returns an account item.
 - (TableViewItem*)accountItem {
-  DCHECK(browser_sync::ProfileSyncService::IsSyncAllowedByFlag());
+  DCHECK(switches::IsSyncAllowedByFlag());
   NSString* text = l10n_util::GetNSString(IDS_SYNC_BASIC_ENCRYPTION_DATA);
   return [self itemWithType:ItemTypeAccount
                        text:text
@@ -119,7 +119,7 @@
 
 // Returns a passphrase item.
 - (TableViewItem*)passphraseItem {
-  DCHECK(browser_sync::ProfileSyncService::IsSyncAllowedByFlag());
+  DCHECK(switches::IsSyncAllowedByFlag());
   NSString* text = l10n_util::GetNSString(IDS_SYNC_FULL_ENCRYPTION_DATA);
   return [self itemWithType:ItemTypePassphrase
                        text:text
@@ -164,7 +164,7 @@
   TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
   switch (item.type) {
     case ItemTypePassphrase: {
-      DCHECK(browser_sync::ProfileSyncService::IsSyncAllowedByFlag());
+      DCHECK(switches::IsSyncAllowedByFlag());
       syncer::SyncService* service =
           ProfileSyncServiceFactory::GetForBrowserState(_browserState);
       if (service->IsEngineInitialized() &&
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index c4023bb..7bcda9d 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -645,6 +645,7 @@
       "test:frame_validator",
       "test:video_player",
       "test:video_player_test_environment",
+      "test:video_player_thumbnail_renderer",
       "//media:test_support",
       "//mojo/core/embedder",
       "//testing/gtest",
diff --git a/media/gpu/test/BUILD.gn b/media/gpu/test/BUILD.gn
index 05f0f8c..afc8688 100644
--- a/media/gpu/test/BUILD.gn
+++ b/media/gpu/test/BUILD.gn
@@ -127,6 +127,23 @@
 
 # TODO(dstaessens@) Make this work on other platforms too.
 if (is_chromeos) {
+  static_library("video_player_thumbnail_renderer") {
+    testonly = true
+    sources = [
+      "video_player/frame_renderer.h",
+      "video_player/frame_renderer_thumbnail.cc",
+      "video_player/frame_renderer_thumbnail.h",
+    ]
+    deps = [
+      ":decode_helpers",
+      "//gpu/command_buffer/common",
+      "//media/gpu",
+      "//ui/gfx/codec:codec",
+      "//ui/gl:gl",
+      "//ui/gl/init:init",
+    ]
+  }
+
   static_library("video_player") {
     testonly = true
     sources = [
diff --git a/media/gpu/test/rendering_helper.cc b/media/gpu/test/rendering_helper.cc
index 2664108..ecfe1a0 100644
--- a/media/gpu/test/rendering_helper.cc
+++ b/media/gpu/test/rendering_helper.cc
@@ -39,38 +39,6 @@
 
 namespace media {
 
-namespace {
-
-// Helper for Shader creation.
-void CreateShader(GLuint program, GLenum type, const char* source, int size) {
-  GLuint shader = glCreateShader(type);
-  glShaderSource(shader, 1, &source, &size);
-  glCompileShader(shader);
-  int result = GL_FALSE;
-  glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
-  if (!result) {
-    char log[4096];
-    glGetShaderInfoLog(shader, base::size(log), NULL, log);
-    LOG(FATAL) << log;
-  }
-  glAttachShader(program, shader);
-  glDeleteShader(shader);
-  CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
-}
-
-void DeleteTexture(uint32_t texture_id) {
-  glDeleteTextures(1, &texture_id);
-  CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
-}
-
-// Helper function to set GL viewport.
-void GLSetViewPort(const gfx::Rect& area) {
-  glViewport(area.x(), area.y(), area.width(), area.height());
-  glScissor(area.x(), area.y(), area.width(), area.height());
-}
-
-}  // namespace
-
 bool RenderingHelper::use_gl_ = false;
 
 VideoFrameTexture::VideoFrameTexture(uint32_t texture_target,
@@ -436,6 +404,39 @@
   }
 }
 
+// static
+void RenderingHelper::DeleteTexture(uint32_t texture_id) {
+  glDeleteTextures(1, &texture_id);
+  CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
+}
+
+// static
+void RenderingHelper::GLSetViewPort(const gfx::Rect& area) {
+  glViewport(area.x(), area.y(), area.width(), area.height());
+  glScissor(area.x(), area.y(), area.width(), area.height());
+}
+
+// static
+void RenderingHelper::CreateShader(GLuint program,
+                                   GLenum type,
+                                   const char* source,
+                                   int size) {
+  GLuint shader = glCreateShader(type);
+  glShaderSource(shader, 1, &source, &size);
+  glCompileShader(shader);
+  int result = GL_FALSE;
+  glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
+  if (!result) {
+    char log[4096];
+    glGetShaderInfoLog(shader, base::size(log), NULL, log);
+    LOG(FATAL) << log;
+  }
+  glAttachShader(program, shader);
+  glDeleteShader(shader);
+  CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
+}
+
+// static
 void RenderingHelper::RenderTexture(uint32_t texture_target,
                                     uint32_t texture_id) {
   // The ExternalOES sampler is bound to GL_TEXTURE1 and the Texture2D sampler
diff --git a/media/gpu/test/rendering_helper.h b/media/gpu/test/rendering_helper.h
index e89ccff..5ff09db 100644
--- a/media/gpu/test/rendering_helper.h
+++ b/media/gpu/test/rendering_helper.h
@@ -35,6 +35,9 @@
 class TextureRef;
 }  // namespace test
 
+// TODO(dstaessens@) Most functionality can be removed from this file when the
+// video_decode_accelerator_unittests are deprecated in favor of the new
+// video_decode_accelerator_test.
 class VideoFrameTexture : public base::RefCounted<VideoFrameTexture> {
  public:
   uint32_t texture_id() const { return texture_id_; }
@@ -120,9 +123,25 @@
   void GetThumbnailsAsRGBA(std::vector<unsigned char>* rgba,
                            base::WaitableEvent* done);
 
+  // Delete the texture with specified |texture_id|.
+  static void DeleteTexture(uint32_t texture_id);
+
+  // Set the GL viewport to the specified |area|.
+  static void GLSetViewPort(const gfx::Rect& area);
+
+  // Create a shader with specified |program| id and |type| by compiling the
+  // shader |source| code with length |size|.
+  static void CreateShader(GLuint program,
+                           GLenum type,
+                           const char* source,
+                           int size);
+
+  // Render |texture_id| to the current view port of the screen using target
+  // |texture_target|.
+  static void RenderTexture(uint32_t texture_target, uint32_t texture_id);
+
  private:
   struct RenderedVideo {
-
     // True if there won't be any new video frames comming.
     bool is_flushing = false;
 
@@ -157,10 +176,6 @@
   void DropOneFrameForAllVideos();
   void ScheduleNextRenderContent();
 
-  // Render |texture_id| to the current view port of the screen using target
-  // |texture_target|.
-  void RenderTexture(uint32_t texture_target, uint32_t texture_id);
-
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
   scoped_refptr<gl::GLContext> gl_context_;
diff --git a/media/gpu/test/video_player/frame_renderer.h b/media/gpu/test/video_player/frame_renderer.h
index 81ccacf2..c56e066 100644
--- a/media/gpu/test/video_player/frame_renderer.h
+++ b/media/gpu/test/video_player/frame_renderer.h
@@ -9,6 +9,7 @@
 
 #include "base/callback_forward.h"
 #include "base/memory/scoped_refptr.h"
+#include "media/base/video_frame.h"
 #include "media/base/video_types.h"
 #include "media/video/picture.h"
 #include "ui/gfx/geometry/size.h"
@@ -42,6 +43,15 @@
   // Render the specified video frame. Once rendering is done the reference to
   // the |video_frame| should be dropped so the video frame can be reused.
   virtual void RenderFrame(scoped_refptr<VideoFrame> video_frame) = 0;
+
+  // Create a texture-backed video frame with specified |pixel_format|, |size|
+  // and |texture_target|. The texture's id will be put in |texture_id|.
+  // TODO(dstaessens@) Remove when allocate mode is removed.
+  virtual scoped_refptr<VideoFrame> CreateVideoFrame(
+      VideoPixelFormat pixel_format,
+      const gfx::Size& size,
+      uint32_t texture_target,
+      uint32_t* texture_id) = 0;
 };
 
 }  // namespace test
diff --git a/media/gpu/test/video_player/frame_renderer_dummy.cc b/media/gpu/test/video_player/frame_renderer_dummy.cc
index 3cb5b18..13d14eb 100644
--- a/media/gpu/test/video_player/frame_renderer_dummy.cc
+++ b/media/gpu/test/video_player/frame_renderer_dummy.cc
@@ -48,5 +48,13 @@
 
 void FrameRendererDummy::RenderFrame(scoped_refptr<VideoFrame> video_frame) {}
 
+scoped_refptr<VideoFrame> FrameRendererDummy::CreateVideoFrame(
+    VideoPixelFormat pixel_format,
+    const gfx::Size& size,
+    uint32_t texture_target,
+    uint32_t* texture_id) {
+  return nullptr;
+}
+
 }  // namespace test
 }  // namespace media
diff --git a/media/gpu/test/video_player/frame_renderer_dummy.h b/media/gpu/test/video_player/frame_renderer_dummy.h
index ce14e5b..10943a44 100644
--- a/media/gpu/test/video_player/frame_renderer_dummy.h
+++ b/media/gpu/test/video_player/frame_renderer_dummy.h
@@ -29,6 +29,10 @@
   void ReleaseGLContext() override;
   gl::GLContext* GetGLContext() override;
   void RenderFrame(scoped_refptr<VideoFrame> video_frame) override;
+  scoped_refptr<VideoFrame> CreateVideoFrame(VideoPixelFormat pixel_format,
+                                             const gfx::Size& size,
+                                             uint32_t texture_target,
+                                             uint32_t* texture_id) override;
 
  private:
   FrameRendererDummy();
diff --git a/media/gpu/test/video_player/frame_renderer_thumbnail.cc b/media/gpu/test/video_player/frame_renderer_thumbnail.cc
new file mode 100644
index 0000000..0463f6d
--- /dev/null
+++ b/media/gpu/test/video_player/frame_renderer_thumbnail.cc
@@ -0,0 +1,430 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/test/video_player/frame_renderer_thumbnail.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "build/build_config.h"
+#include "media/gpu/test/rendering_helper.h"
+#include "media/gpu/test/video_decode_accelerator_unittest_helpers.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_surface_egl.h"
+#include "ui/gl/init/gl_factory.h"
+
+namespace media {
+namespace test {
+
+namespace {
+
+// Size of the large image to which the thumbnails will be rendered.
+constexpr gfx::Size kThumbnailsPageSize(1600, 1200);
+// Size of the individual thumbnails that will be rendered.
+constexpr gfx::Size kThumbnailSize(160, 120);
+
+// Default file path used to store the thumbnail image.
+constexpr const base::FilePath::CharType* kDefaultOutputPath =
+    FILE_PATH_LITERAL("thumbnail.png");
+
+// Vertex shader used to render thumbnails.
+constexpr char kVertexShader[] =
+    "varying vec2 interp_tc;\n"
+    "attribute vec4 in_pos;\n"
+    "attribute vec2 in_tc;\n"
+    "uniform bool tex_flip; void main() {\n"
+    "  if (tex_flip)\n"
+    "    interp_tc = vec2(in_tc.x, 1.0 - in_tc.y);\n"
+    "  else\n"
+    "   interp_tc = in_tc;\n"
+    "  gl_Position = in_pos;\n"
+    "}\n";
+
+// Fragment shader used to render thumbnails.
+#if !defined(OS_WIN)
+constexpr char kFragmentShader[] =
+    "#extension GL_OES_EGL_image_external : enable\n"
+    "precision mediump float;\n"
+    "varying vec2 interp_tc;\n"
+    "uniform sampler2D tex;\n"
+    "#ifdef GL_OES_EGL_image_external\n"
+    "uniform samplerExternalOES tex_external;\n"
+    "#endif\n"
+    "void main() {\n"
+    "  vec4 color = texture2D(tex, interp_tc);\n"
+    "#ifdef GL_OES_EGL_image_external\n"
+    "  color += texture2D(tex_external, interp_tc);\n"
+    "#endif\n"
+    "  gl_FragColor = color;\n"
+    "}\n";
+#else
+constexpr char kFragmentShader[] =
+    "#ifdef GL_ES\n"
+    "precision mediump float;\n"
+    "#endif\n"
+    "varying vec2 interp_tc;\n"
+    "uniform sampler2D tex;\n"
+    "void main() {\n"
+    "  gl_FragColor = texture2D(tex, interp_tc);\n"
+    "}\n";
+#endif
+
+GLuint CreateTexture(GLenum texture_target, const gfx::Size& size) {
+  GLuint texture_id;
+  glGenTextures(1, &texture_id);
+  glBindTexture(texture_target, texture_id);
+  if (texture_target == GL_TEXTURE_2D) {
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
+                 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+  }
+
+  glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  // OpenGLES2.0.25 section 3.8.2 requires CLAMP_TO_EDGE for NPOT textures.
+  glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+  CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
+  return texture_id;
+}
+
+// Helper class to automatically acquire and release the GL context.
+class AutoGLContext {
+ public:
+  explicit AutoGLContext(FrameRenderer* const frame_renderer)
+      : frame_renderer_(frame_renderer) {
+    frame_renderer_->AcquireGLContext();
+  }
+  ~AutoGLContext() { frame_renderer_->ReleaseGLContext(); }
+
+ private:
+  FrameRenderer* const frame_renderer_;
+};
+
+}  // namespace
+
+bool FrameRendererThumbnail::gl_initialized_ = false;
+
+FrameRendererThumbnail::FrameRendererThumbnail(
+    const std::vector<std::string>& thumbnail_checksums)
+    : frame_count_(0),
+      thumbnail_checksums_(thumbnail_checksums),
+      thumbnails_fbo_id_(0),
+      thumbnails_texture_id_(0),
+      vertex_buffer_(0),
+      program_(0) {
+  DETACH_FROM_SEQUENCE(renderer_sequence_checker_);
+}
+
+FrameRendererThumbnail::~FrameRendererThumbnail() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  base::AutoLock auto_lock(renderer_lock_);
+  DestroyThumbnailImage();
+  gl_context_ = nullptr;
+  gl_surface_ = nullptr;
+
+  CHECK(mailbox_texture_map_.empty());
+}
+
+// static
+std::unique_ptr<FrameRendererThumbnail> FrameRendererThumbnail::Create(
+    const base::FilePath& video_file_path) {
+  // Read thumbnail checksums from file.
+  std::vector<std::string> thumbnail_checksums =
+      media::test::ReadGoldenThumbnailMD5s(
+          video_file_path.AddExtension(FILE_PATH_LITERAL(".md5")));
+
+  auto frame_renderer =
+      base::WrapUnique(new FrameRendererThumbnail(thumbnail_checksums));
+  frame_renderer->Initialize(video_file_path);
+  return frame_renderer;
+}
+
+void FrameRendererThumbnail::AcquireGLContext() {
+  gl_context_lock_.Acquire();
+  CHECK(gl_context_->MakeCurrent(gl_surface_.get()));
+}
+
+void FrameRendererThumbnail::ReleaseGLContext() {
+  gl_context_lock_.AssertAcquired();
+  gl_context_->ReleaseCurrent(gl_surface_.get());
+  gl_context_lock_.Release();
+}
+
+gl::GLContext* FrameRendererThumbnail::GetGLContext() {
+  gl_context_lock_.AssertAcquired();
+  return gl_context_.get();
+}
+
+void FrameRendererThumbnail::RenderFrame(
+    scoped_refptr<VideoFrame> video_frame) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(renderer_sequence_checker_);
+
+  // Find the texture associated with the video frame's mailbox.
+  base::AutoLock auto_lock(renderer_lock_);
+  const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0);
+  const gpu::Mailbox& mailbox = mailbox_holder.mailbox;
+  auto it = mailbox_texture_map_.find(mailbox);
+  ASSERT_NE(it, mailbox_texture_map_.end());
+
+  RenderThumbnail(mailbox_holder.texture_target, it->second);
+}
+
+scoped_refptr<VideoFrame> FrameRendererThumbnail::CreateVideoFrame(
+    VideoPixelFormat pixel_format,
+    const gfx::Size& texture_size,
+    uint32_t texture_target,
+    uint32_t* texture_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(renderer_sequence_checker_);
+
+  // Create a mailbox.
+  gpu::Mailbox mailbox = gpu::Mailbox::Generate();
+  gpu::MailboxHolder mailbox_holders[media::VideoFrame::kMaxPlanes];
+  mailbox_holders[0] =
+      gpu::MailboxHolder(mailbox, gpu::SyncToken(), texture_target);
+
+  // Create a new video frame associated with the mailbox.
+  base::OnceCallback<void(const gpu::SyncToken&)> mailbox_holder_release_cb =
+      base::BindOnce(&FrameRendererThumbnail::DeleteTexture,
+                     base::Unretained(this), mailbox);
+  scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTextures(
+      pixel_format, mailbox_holders, std::move(mailbox_holder_release_cb),
+      texture_size, gfx::Rect(texture_size), texture_size, base::TimeDelta());
+
+  // Create a texture and associate it with the mailbox.
+  {
+    AutoGLContext auto_gl_context(this);
+    *texture_id = CreateTexture(texture_target, texture_size);
+  }
+
+  base::AutoLock auto_lock(renderer_lock_);
+  mailbox_texture_map_.insert(std::make_pair(mailbox, *texture_id));
+
+  return frame;
+}
+
+bool FrameRendererThumbnail::ValidateThumbnail() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  base::AutoLock auto_lock(renderer_lock_);
+  const std::vector<uint8_t> rgba = ConvertThumbnailToRGBA();
+
+  // Convert the thumbnail from RGBA to RGB.
+  std::vector<uint8_t> rgb;
+  EXPECT_EQ(media::test::ConvertRGBAToRGB(rgba, &rgb), true)
+      << "RGBA frame has incorrect alpha";
+
+  // Calculate the thumbnail's checksum and compare it to golden values.
+  std::string md5_string = base::MD5String(
+      base::StringPiece(reinterpret_cast<char*>(&rgb[0]), rgb.size()));
+  bool is_valid_thumbnail =
+      base::ContainsValue(thumbnail_checksums_, md5_string);
+
+  return is_valid_thumbnail;
+}
+
+void FrameRendererThumbnail::SaveThumbnail() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  base::AutoLock auto_lock(renderer_lock_);
+  const std::vector<uint8_t> rgba = ConvertThumbnailToRGBA();
+
+  // Convert raw RGBA into PNG for export.
+  std::vector<unsigned char> png;
+  gfx::PNGCodec::Encode(&rgba[0], gfx::PNGCodec::FORMAT_RGBA,
+                        kThumbnailsPageSize, kThumbnailsPageSize.width() * 4,
+                        true, std::vector<gfx::PNGCodec::Comment>(), &png);
+
+  base::FilePath filepath(kDefaultOutputPath);
+  int num_bytes =
+      base::WriteFile(filepath, reinterpret_cast<char*>(&png[0]), png.size());
+  ASSERT_NE(-1, num_bytes);
+  EXPECT_EQ(static_cast<size_t>(num_bytes), png.size());
+}
+
+void FrameRendererThumbnail::Initialize(const base::FilePath& video_file_path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  // Initialize GL rendering and create GL context.
+  if (!gl_initialized_) {
+    if (!gl::init::InitializeGLOneOff())
+      LOG(FATAL) << "Could not initialize GL";
+    gl_initialized_ = true;
+  }
+  gl_surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
+  gl_context_ = gl::init::CreateGLContext(nullptr, gl_surface_.get(),
+                                          gl::GLContextAttribs());
+
+  base::AutoLock auto_lock(renderer_lock_);
+  InitializeThumbnailImage();
+}
+
+// TODO(dstaessens@) This code is mostly duplicated from
+// RenderingHelper::Initialize(), as that code is unfortunately too inflexible
+// to reuse here. But most of the code in rendering helper can be removed soon
+// when the video_decoder_accelerator_unittests get deprecated.
+void FrameRendererThumbnail::InitializeThumbnailImage() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  AutoGLContext auto_gl_context(this);
+  GLint max_texture_size;
+  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
+  CHECK_GE(max_texture_size, kThumbnailsPageSize.width());
+  CHECK_GE(max_texture_size, kThumbnailsPageSize.height());
+
+  thumbnails_fbo_size_ = kThumbnailsPageSize;
+  thumbnail_size_ = kThumbnailSize;
+
+  glGenFramebuffersEXT(1, &thumbnails_fbo_id_);
+  glGenTextures(1, &thumbnails_texture_id_);
+  glBindTexture(GL_TEXTURE_2D, thumbnails_texture_id_);
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, thumbnails_fbo_size_.width(),
+               thumbnails_fbo_size_.height(), 0, GL_RGB,
+               GL_UNSIGNED_SHORT_5_6_5, NULL);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glBindTexture(GL_TEXTURE_2D, 0);
+
+  glBindFramebufferEXT(GL_FRAMEBUFFER, thumbnails_fbo_id_);
+  glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                            thumbnails_texture_id_, 0);
+
+  GLenum fb_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
+  CHECK(fb_status == GL_FRAMEBUFFER_COMPLETE) << fb_status;
+  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+  glClear(GL_COLOR_BUFFER_BIT);
+  glBindFramebufferEXT(GL_FRAMEBUFFER,
+                       gl_surface_->GetBackingFramebufferObject());
+
+  // These vertices and texture coords map (0,0) in the texture to the bottom
+  // left of the viewport. Since we get the video frames with the the top left
+  // at (0,0) we need to flip the texture y coordinate in the vertex shader for
+  // this to be rendered the right way up. In the case of thumbnail rendering we
+  // use the same vertex shader to render the FBO to the screen, where we do not
+  // want this flipping. Vertices are 2 floats for position and 2 floats for
+  // texcoord each.
+  const float kVertices[] = {
+      -1, 1,  0, 1,  // Vertex 0
+      -1, -1, 0, 0,  // Vertex 1
+      1,  1,  1, 1,  // Vertex 2
+      1,  -1, 1, 0,  // Vertex 3
+  };
+  const GLvoid* kVertexPositionOffset = 0;
+  const GLvoid* kVertexTexcoordOffset =
+      reinterpret_cast<GLvoid*>(sizeof(float) * 2);
+  const GLsizei kVertexStride = sizeof(float) * 4;
+
+  glGenBuffersARB(1, &vertex_buffer_);
+  glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
+  glBufferData(GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW);
+
+  program_ = glCreateProgram();
+  RenderingHelper::CreateShader(program_, GL_VERTEX_SHADER, kVertexShader,
+                                base::size(kVertexShader));
+  RenderingHelper::CreateShader(program_, GL_FRAGMENT_SHADER, kFragmentShader,
+                                base::size(kFragmentShader));
+  glLinkProgram(program_);
+  GLint result = GL_FALSE;
+  glGetProgramiv(program_, GL_LINK_STATUS, &result);
+  if (!result) {
+    constexpr GLsizei kLogBufferSize = 4096;
+    char log[kLogBufferSize];
+    glGetShaderInfoLog(program_, kLogBufferSize, NULL, log);
+    LOG(FATAL) << log;
+  }
+  glUseProgram(program_);
+  glDeleteProgram(program_);
+
+  glUniform1i(glGetUniformLocation(program_, "tex_flip"), 0);
+  glUniform1i(glGetUniformLocation(program_, "tex"), 0);
+  GLint tex_external = glGetUniformLocation(program_, "tex_external");
+  if (tex_external != -1) {
+    glUniform1i(tex_external, 1);
+  }
+  GLint pos_location = glGetAttribLocation(program_, "in_pos");
+  glEnableVertexAttribArray(pos_location);
+  glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, kVertexStride,
+                        kVertexPositionOffset);
+  GLint tc_location = glGetAttribLocation(program_, "in_tc");
+  glEnableVertexAttribArray(tc_location);
+  glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, kVertexStride,
+                        kVertexTexcoordOffset);
+
+  // Unbind the vertex buffer
+  glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void FrameRendererThumbnail::DestroyThumbnailImage() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  AutoGLContext auto_gl_context(this);
+  glDeleteTextures(1, &thumbnails_texture_id_);
+  glDeleteFramebuffersEXT(1, &thumbnails_fbo_id_);
+  glDeleteBuffersARB(1, &vertex_buffer_);
+}
+
+void FrameRendererThumbnail::RenderThumbnail(uint32_t texture_target,
+                                             uint32_t texture_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(renderer_sequence_checker_);
+
+  const int width = thumbnail_size_.width();
+  const int height = thumbnail_size_.height();
+  const int thumbnails_in_row = thumbnails_fbo_size_.width() / width;
+  const int thumbnails_in_column = thumbnails_fbo_size_.height() / height;
+  const int row = (frame_count_ / thumbnails_in_row) % thumbnails_in_column;
+  const int col = frame_count_ % thumbnails_in_row;
+  gfx::Rect area(col * width, row * height, width, height);
+
+  AutoGLContext auto_gl_context(this);
+  glUniform1i(glGetUniformLocation(program_, "tex_flip"), 0);
+  glBindFramebufferEXT(GL_FRAMEBUFFER, thumbnails_fbo_id_);
+  RenderingHelper::GLSetViewPort(area);
+  RenderingHelper::RenderTexture(texture_target, texture_id);
+  glBindFramebufferEXT(GL_FRAMEBUFFER,
+                       gl_surface_->GetBackingFramebufferObject());
+  // We need to flush the GL commands before returning the thumbnail texture to
+  // the decoder.
+  glFlush();
+
+  ++frame_count_;
+}
+
+const std::vector<uint8_t> FrameRendererThumbnail::ConvertThumbnailToRGBA() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  AutoGLContext auto_gl_context(this);
+  std::vector<uint8_t> rgba;
+  const size_t num_pixels = thumbnails_fbo_size_.GetArea();
+  rgba.resize(num_pixels * 4);
+  glBindFramebufferEXT(GL_FRAMEBUFFER, thumbnails_fbo_id_);
+  glPixelStorei(GL_PACK_ALIGNMENT, 1);
+  // We can only count on GL_RGBA/GL_UNSIGNED_BYTE support.
+  glReadPixels(0, 0, thumbnails_fbo_size_.width(),
+               thumbnails_fbo_size_.height(), GL_RGBA, GL_UNSIGNED_BYTE,
+               &(rgba)[0]);
+  glBindFramebufferEXT(GL_FRAMEBUFFER,
+                       gl_surface_->GetBackingFramebufferObject());
+
+  return rgba;
+}
+
+void FrameRendererThumbnail::DeleteTexture(const gpu::Mailbox& mailbox,
+                                           const gpu::SyncToken&) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  base::AutoLock auto_lock(renderer_lock_);
+  auto it = mailbox_texture_map_.find(mailbox);
+  ASSERT_NE(it, mailbox_texture_map_.end());
+  uint32_t texture_id = it->second;
+  mailbox_texture_map_.erase(mailbox);
+
+  RenderingHelper::DeleteTexture(texture_id);
+}
+
+}  // namespace test
+}  // namespace media
diff --git a/media/gpu/test/video_player/frame_renderer_thumbnail.h b/media/gpu/test/video_player/frame_renderer_thumbnail.h
new file mode 100644
index 0000000..b3a7437
--- /dev/null
+++ b/media/gpu/test/video_player/frame_renderer_thumbnail.h
@@ -0,0 +1,130 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_TEST_VIDEO_PLAYER_FRAME_RENDERER_THUMBNAIL_H_
+#define MEDIA_GPU_TEST_VIDEO_PLAYER_FRAME_RENDERER_THUMBNAIL_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/sequence_checker.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/thread_annotations.h"
+#include "base/threading/thread.h"
+#include "gpu/command_buffer/common/gl2_types.h"
+#include "media/gpu/test/video_player/frame_renderer.h"
+
+namespace gl {
+
+class GLContext;
+class GLSurface;
+
+}  // namespace gl
+
+namespace media {
+namespace test {
+
+// The thumbnail frame renderer draws a thumbnail of each frame into a large
+// image containing 10x10 thumbnails. The checksum of this image can then be
+// compared to a golden value.
+// Rendering introduces small platform-dependant differences, so multiple golden
+// values need to be maintained. The thumbnail frame renderer should only be
+// used on older platforms that are not supported by the FrameValidator, and
+// will be deprecated soon.
+class FrameRendererThumbnail : public FrameRenderer {
+ public:
+  ~FrameRendererThumbnail() override;
+
+  // Create an instance of the thumbnail frame renderer. The |video_file_path|
+  // should point to a file containing all golden thumbnail hashes for the video
+  // being rendered.
+  static std::unique_ptr<FrameRendererThumbnail> Create(
+      const base::FilePath& video_file_path);
+
+  // FrameRenderer implementation
+  // Acquire the active GL context. This will claim |gl_context_lock_|.
+  void AcquireGLContext() override;
+  // Release the active GL context. This will release |gl_context_lock_|.
+  void ReleaseGLContext() override;
+  // Get the active GL context. This requires holding |gl_context_lock_|.
+  gl::GLContext* GetGLContext() override;
+  void RenderFrame(scoped_refptr<VideoFrame> video_frame) override;
+  scoped_refptr<VideoFrame> CreateVideoFrame(VideoPixelFormat pixel_format,
+                                             const gfx::Size& texture_size,
+                                             uint32_t texture_target,
+                                             uint32_t* texture_id) override;
+
+  // Validate the thumbnail image by comparing it against known golden values.
+  bool ValidateThumbnail();
+  // Save the thumbnail image to disk.
+  void SaveThumbnail();
+
+ private:
+  explicit FrameRendererThumbnail(
+      const std::vector<std::string>& thumbnail_checksums);
+
+  // Initialize the frame renderer, performs all rendering-related setup.
+  void Initialize(const base::FilePath& video_file_path);
+
+  // Initialize the thumbnail image the frame thumbnails will be rendered to.
+  void InitializeThumbnailImage() EXCLUSIVE_LOCKS_REQUIRED(renderer_lock_);
+  // Destroy the thumbnail image.
+  void DestroyThumbnailImage() EXCLUSIVE_LOCKS_REQUIRED(renderer_lock_);
+  // Render the texture with specified |texture_id| to the thumbnail image.
+  void RenderThumbnail(uint32_t texture_target, uint32_t texture_id)
+      EXCLUSIVE_LOCKS_REQUIRED(renderer_lock_);
+  // Convert the thumbnail image to RGBA.
+  const std::vector<uint8_t> ConvertThumbnailToRGBA()
+      EXCLUSIVE_LOCKS_REQUIRED(renderer_lock_);
+
+  // Destroy the texture associated with the specified |mailbox|.
+  void DeleteTexture(const gpu::Mailbox& mailbox, const gpu::SyncToken&);
+
+  // The number of frames rendered so far.
+  size_t frame_count_ GUARDED_BY(renderer_lock_);
+
+  // The list of thumbnail checksums for all platforms.
+  const std::vector<std::string> thumbnail_checksums_;
+  // Map between mailboxes and texture id's.
+  std::map<gpu::Mailbox, uint32_t> mailbox_texture_map_
+      GUARDED_BY(renderer_lock_);
+
+  // Id of the frame buffer used to render the thumbnails image.
+  GLuint thumbnails_fbo_id_ GUARDED_BY(renderer_lock_);
+  // Size of the frame buffer used to render the thumbnails image.
+  gfx::Size thumbnails_fbo_size_ GUARDED_BY(renderer_lock_);
+  // Texture id of the thumbnails image.
+  GLuint thumbnails_texture_id_ GUARDED_BY(renderer_lock_);
+  // Size of the individual thumbnails rendered to the thumbnails image.
+  gfx::Size thumbnail_size_ GUARDED_BY(renderer_lock_);
+  // Vertex buffer used to render thumbnails to the thumbnails image.
+  GLuint vertex_buffer_ GUARDED_BY(renderer_lock_);
+  // Shader program used to render thumbnails to the thumbnails image.
+  GLuint program_ GUARDED_BY(renderer_lock_);
+
+  // Lock protecting variables accessed on both renderer and client thread.
+  mutable base::Lock renderer_lock_;
+
+  // Lock protecting access to the gl context.
+  mutable base::Lock gl_context_lock_;
+  // GL context used for rendering.
+  scoped_refptr<gl::GLContext> gl_context_;
+  // GL surface used for rendering.
+  scoped_refptr<gl::GLSurface> gl_surface_;
+
+  // Whether GL was initialized, as it should only happen once.
+  static bool gl_initialized_;
+
+  SEQUENCE_CHECKER(client_sequence_checker_);
+  SEQUENCE_CHECKER(renderer_sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(FrameRendererThumbnail);
+};
+
+}  // namespace test
+}  // namespace media
+
+#endif  // MEDIA_GPU_TEST_VIDEO_PLAYER_FRAME_RENDERER_THUMBNAIL_H_
diff --git a/media/gpu/test/video_player/video.cc b/media/gpu/test/video_player/video.cc
index dc01d68..47a8e3f 100644
--- a/media/gpu/test/video_player/video.cc
+++ b/media/gpu/test/video_player/video.cc
@@ -78,6 +78,10 @@
   return data_.size() > 0;
 }
 
+const base::FilePath& Video::FilePath() const {
+  return file_path_;
+}
+
 const std::vector<uint8_t>& Video::Data() const {
   return data_;
 }
diff --git a/media/gpu/test/video_player/video.h b/media/gpu/test/video_player/video.h
index b5ae6ff..155355a2 100644
--- a/media/gpu/test/video_player/video.h
+++ b/media/gpu/test/video_player/video.h
@@ -29,6 +29,8 @@
   // Returns true if the video file was loaded.
   bool IsLoaded() const;
 
+  // Get the video file path.
+  const base::FilePath& FilePath() const;
   // Get the video data, will be empty if the video hasn't been loaded yet.
   const std::vector<uint8_t>& Data() const;
 
diff --git a/media/gpu/test/video_player/video_decoder_client.cc b/media/gpu/test/video_player/video_decoder_client.cc
index 69fe89b2..c179f59 100644
--- a/media/gpu/test/video_player/video_decoder_client.cc
+++ b/media/gpu/test/video_player/video_decoder_client.cc
@@ -45,6 +45,12 @@
   DestroyDecoder();
   decoder_client_thread_.Stop();
 
+  // Clear video frames, triggering associated destruction callbacks while we
+  // still have a GLcontext.
+  frame_renderer_->AcquireGLContext();
+  video_frames_.clear();
+  frame_renderer_->ReleaseGLContext();
+
   // Wait until the frame processors are done, before destroying them. As the
   // decoder has been destroyed no new frames will be sent to the processors.
   WaitForFrameProcessors();
@@ -114,6 +120,10 @@
   return success;
 }
 
+FrameRenderer* VideoDecoderClient::GetFrameRenderer() const {
+  return frame_renderer_.get();
+}
+
 void VideoDecoderClient::Play() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(video_player_sequence_checker_);
 
@@ -148,23 +158,48 @@
             << " picture buffers with size " << size.height() << "x"
             << size.height();
 
-  // Create a set of picture buffers and give them to the decoder.
-  std::vector<PictureBuffer> picture_buffers;
-  for (uint32_t i = 0; i < requested_num_of_buffers; ++i)
-    picture_buffers.emplace_back(GetNextPictureBufferId(), size);
-  decoder_->AssignPictureBuffers(picture_buffers);
+  // If using import mode, create a set of DMABuf-backed video frames.
+  if (decoder_client_config_.allocation_mode == AllocationMode::kImport) {
+    std::vector<PictureBuffer> picture_buffers;
+    for (uint32_t i = 0; i < requested_num_of_buffers; ++i) {
+      picture_buffers.emplace_back(GetNextPictureBufferId(), size);
+    }
+    decoder_->AssignPictureBuffers(picture_buffers);
 
-  // Create a video frame for each of the picture buffers and provide memory
-  // handles to the video frame's data to the decoder.
-  for (const PictureBuffer& picture_buffer : picture_buffers) {
-    scoped_refptr<VideoFrame> video_frame =
-        CreatePlatformVideoFrame(pixel_format, size);
-    LOG_ASSERT(video_frame) << "Failed to create video frame";
-    video_frames_.emplace(picture_buffer.id(), video_frame);
-    gfx::GpuMemoryBufferHandle handle =
-        CreateGpuMemoryBufferHandle(video_frame);
-    LOG_ASSERT(!handle.is_null()) << "Failed to create GPU memory handle";
-    decoder_->ImportBufferForPicture(picture_buffer.id(), pixel_format, handle);
+    // Create a video frame for each of the picture buffers and provide memory
+    // handles to the video frame's data to the decoder.
+    for (const PictureBuffer& picture_buffer : picture_buffers) {
+      scoped_refptr<VideoFrame> video_frame =
+          CreatePlatformVideoFrame(pixel_format, size);
+      LOG_ASSERT(video_frame) << "Failed to create video frame";
+      video_frames_.emplace(picture_buffer.id(), video_frame);
+      gfx::GpuMemoryBufferHandle handle =
+          CreateGpuMemoryBufferHandle(video_frame);
+      LOG_ASSERT(!handle.is_null()) << "Failed to create GPU memory handle";
+      decoder_->ImportBufferForPicture(picture_buffer.id(), pixel_format,
+                                       handle);
+    }
+  }
+
+  // If using allocate mode, request a set of texture-backed video frames from
+  // the renderer.
+  if (decoder_client_config_.allocation_mode == AllocationMode::kAllocate) {
+    std::vector<PictureBuffer> picture_buffers;
+    for (uint32_t i = 0; i < requested_num_of_buffers; ++i) {
+      uint32_t texture_id;
+      auto video_frame = frame_renderer_->CreateVideoFrame(
+          pixel_format, size, texture_target, &texture_id);
+      LOG_ASSERT(video_frame) << "Failed to create video frame";
+      int32_t picture_buffer_id = GetNextPictureBufferId();
+      PictureBuffer::TextureIds texture_ids(1, texture_id);
+      picture_buffers.emplace_back(picture_buffer_id, size, texture_ids,
+                                   texture_ids, texture_target, pixel_format);
+      video_frames_.emplace(picture_buffer_id, std::move(video_frame));
+    }
+    // The decoder requires an active GL context to allocate memory.
+    frame_renderer_->AcquireGLContext();
+    decoder_->AssignPictureBuffers(picture_buffers);
+    frame_renderer_->ReleaseGLContext();
   }
 }
 
@@ -183,24 +218,38 @@
   LOG_ASSERT(it != video_frames_.end());
   scoped_refptr<VideoFrame> video_frame = it->second;
 
-  // Wrap the video frame in another video frame that calls
-  // ReusePictureBufferTask() upon destruction. When the renderer is done using
-  // the video frame, the associated picture buffer will automatically be
-  // flagged for reuse.
-  base::OnceClosure delete_cb = BindToCurrentLoop(
-      base::BindOnce(&VideoDecoderClient::ReusePictureBufferTask,
-                     base::Unretained(this), picture.picture_buffer_id()));
+  // When using import mode, we wrap the video frame in another video frame that
+  // calls ReusePictureBufferTask() upon destruction. When the renderer and
+  // video frame processors are done using the video frame, the associated
+  // picture buffer will automatically be flagged for reuse.
+  if (decoder_client_config_.allocation_mode == AllocationMode::kImport) {
+    base::OnceClosure delete_cb = BindToCurrentLoop(
+        base::BindOnce(&VideoDecoderClient::ReusePictureBufferTask,
+                       base::Unretained(this), picture.picture_buffer_id()));
 
-  scoped_refptr<VideoFrame> wrapped_video_frame = VideoFrame::WrapVideoFrame(
-      video_frame, video_frame->format(), video_frame->visible_rect(),
-      video_frame->visible_rect().size());
-  wrapped_video_frame->AddDestructionObserver(std::move(delete_cb));
+    scoped_refptr<VideoFrame> wrapped_video_frame = VideoFrame::WrapVideoFrame(
+        video_frame, video_frame->format(), video_frame->visible_rect(),
+        video_frame->visible_rect().size());
+    wrapped_video_frame->AddDestructionObserver(std::move(delete_cb));
 
-  frame_renderer_->RenderFrame(wrapped_video_frame);
+    frame_renderer_->RenderFrame(wrapped_video_frame);
 
-  for (auto& frame_processor : frame_processors_)
-    frame_processor->ProcessVideoFrame(wrapped_video_frame,
-                                       current_frame_index_);
+    for (auto& frame_processor : frame_processors_)
+      frame_processor->ProcessVideoFrame(wrapped_video_frame,
+                                         current_frame_index_);
+  }
+
+  // When using allocate mode, direct texture memory access is not supported.
+  // Since this is required by the video frame processors we can't use these
+  // here. Wrapping a video frame inside another video frame is also not
+  // supported, so we have to render the frame and return the picture buffer
+  // synchronously here. See http://crbug/362521.
+  if (decoder_client_config_.allocation_mode == AllocationMode::kAllocate) {
+    frame_renderer_->RenderFrame(video_frame);
+    ReusePictureBufferTask(picture.picture_buffer_id());
+    return;
+  }
+
   current_frame_index_++;
 }
 
diff --git a/media/gpu/test/video_player/video_decoder_client.h b/media/gpu/test/video_player/video_decoder_client.h
index b4e432a..0c153d1 100644
--- a/media/gpu/test/video_player/video_decoder_client.h
+++ b/media/gpu/test/video_player/video_decoder_client.h
@@ -27,11 +27,20 @@
 class FrameRenderer;
 class VideoFrameProcessor;
 
+// TODO(dstaessens@) Remove allocation mode, temporary added here so we can
+// support the thumbnail test for older platforms that don't support import.
+enum class AllocationMode {
+  kImport,    // Client allocates video frame memory.
+  kAllocate,  // Video decoder allocates video frame memory.
+};
+
 // Video decoder client configuration.
 struct VideoDecoderClientConfig {
   // The maximum number of bitstream buffer decodes that can be requested
   // without waiting for the result of the previous decode requests.
   size_t max_outstanding_decode_requests = 1;
+  // How the pictures buffers should be allocated.
+  AllocationMode allocation_mode = AllocationMode::kImport;
 };
 
 // The video decoder client is responsible for the communication between the
@@ -70,6 +79,8 @@
   // Wait until all frame processors have finished processing. Returns whether
   // processing was successful.
   bool WaitForFrameProcessors();
+  // Get the frame renderer associated with the video decoder client.
+  FrameRenderer* GetFrameRenderer() const;
 
   // Start decoding the video stream, decoder should be idle when this function
   // is called. This function is non-blocking, for each frame decoded a
diff --git a/media/gpu/test/video_player/video_player.cc b/media/gpu/test/video_player/video_player.cc
index 2fb37a3..87b014c3 100644
--- a/media/gpu/test/video_player/video_player.cc
+++ b/media/gpu/test/video_player/video_player.cc
@@ -57,12 +57,15 @@
       event_cb, std::move(frame_renderer), std::move(frame_processors), config);
   CHECK(decoder_client_) << "Failed to create decoder client";
 
-  // Create a decoder for the specified video. We'll always use import mode as
-  // this is the only mode supported by the media::VideoDecoder interface, which
-  // the video decoders are being migrated to.
+  // Create a decoder for the specified video.
+  // TODO(dstaessens@) Remove support for allocate mode, and always use import
+  // mode. Support for allocate mode is temporary maintained for older platforms
+  // that don't support import mode.
   VideoDecodeAccelerator::Config decoder_config(video->Profile());
   decoder_config.output_mode =
-      VideoDecodeAccelerator::Config::OutputMode::IMPORT;
+      config.allocation_mode == AllocationMode::kImport
+          ? VideoDecodeAccelerator::Config::OutputMode::IMPORT
+          : VideoDecodeAccelerator::Config::OutputMode::ALLOCATE;
   decoder_client_->CreateDecoder(decoder_config, video->Data());
 
   video_ = video;
@@ -129,6 +132,10 @@
   return video_player_state_;
 }
 
+FrameRenderer* VideoPlayer::GetFrameRenderer() const {
+  return decoder_client_->GetFrameRenderer();
+}
+
 bool VideoPlayer::WaitForEvent(VideoPlayerEvent event,
                                size_t times,
                                base::TimeDelta max_wait) {
diff --git a/media/gpu/test/video_player/video_player.h b/media/gpu/test/video_player/video_player.h
index bdfeb22..da5a6112 100644
--- a/media/gpu/test/video_player/video_player.h
+++ b/media/gpu/test/video_player/video_player.h
@@ -84,6 +84,8 @@
   size_t GetCurrentFrame() const;
   // Get the current state of the video player.
   VideoPlayerState GetState() const;
+  // Get the frame renderer associated with the video player.
+  FrameRenderer* GetFrameRenderer() const;
 
   // Wait for an event to occur the specified number of times. All events that
   // occurred since last calling this function will be taken into account. All
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
index 6cdc015..4ba7f87 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
@@ -1610,14 +1610,14 @@
               << " not in use (anymore?).";
     return;
   }
-  V4L2ReadableBufferRef buffer = std::move(iter->second);
-  DCHECK_EQ(buffers_at_client_.count(picture_buffer_id), 1u);
-  buffers_at_client_.erase(iter);
 
-  // Take ownership of the EGL fence.
+  // Take ownership of the EGL fence and keep the buffer out of the game until
+  // the fence signals.
   if (egl_fence)
     buffers_awaiting_fence_.emplace(
-        std::make_pair(std::move(egl_fence), std::move(buffer)));
+        std::make_pair(std::move(egl_fence), std::move(iter->second)));
+
+  buffers_at_client_.erase(iter);
 
   // We got a buffer back, so enqueue it back.
   Enqueue();
@@ -1885,6 +1885,9 @@
     decoder_input_queue_.pop_front();
   decoder_flushing_ = false;
 
+  // First liberate all the frames held by the client.
+  buffers_at_client_.clear();
+
   image_processor_ = nullptr;
   while (!buffers_at_ip_.empty())
     buffers_at_ip_.pop();
@@ -1996,6 +1999,8 @@
     return;
   }
 
+  buffers_at_client_.clear();
+
   image_processor_ = nullptr;
 
   if (!DestroyOutputBuffers()) {
@@ -2584,10 +2589,6 @@
   while (!buffers_awaiting_fence_.empty())
     buffers_awaiting_fence_.pop();
 
-  // TODO(acourbot@) the client should properly drop all references to the
-  // frames it holds instead!
-  buffers_at_client_.clear();
-
   if (!output_queue_->DeallocateBuffers()) {
     NOTIFY_ERROR(PLATFORM_FAILURE);
     success = false;
@@ -2601,7 +2602,8 @@
 void V4L2VideoDecodeAccelerator::SendBufferToClient(
     size_t output_buffer_index,
     int32_t bitstream_buffer_id,
-    V4L2ReadableBufferRef vda_buffer) {
+    V4L2ReadableBufferRef vda_buffer,
+    scoped_refptr<VideoFrame> frame) {
   DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK_GE(bitstream_buffer_id, 0);
   OutputRecord& output_record = output_buffer_map_[output_buffer_index];
@@ -2610,7 +2612,9 @@
   // We need to keep the VDA buffer for now, as the IP still needs to be told
   // which buffer to use so we cannot use this buffer index before the client
   // has returned the corresponding IP buffer.
-  buffers_at_client_.emplace(output_record.picture_id, std::move(vda_buffer));
+  buffers_at_client_.emplace(
+      output_record.picture_id,
+      std::make_pair(std::move(vda_buffer), std::move(frame)));
   // TODO(hubbe): Insert correct color space. http://crbug.com/647725
   const Picture picture(output_record.picture_id, bitstream_buffer_id,
                         gfx::Rect(visible_size_), gfx::ColorSpace(), false);
@@ -2719,8 +2723,7 @@
     child_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&V4L2VideoDecodeAccelerator::CreateEGLImageFor,
-                       weak_this_, ip_buffer_index,
-                       ip_output_record.picture_id,
+                       weak_this_, ip_buffer_index, ip_output_record.picture_id,
                        media::DuplicateFDs(frame->DmabufFds()),
                        ip_output_record.texture_id, egl_image_size_,
                        egl_image_format_fourcc_));
@@ -2734,7 +2737,7 @@
   buffers_at_ip_.pop();
 
   SendBufferToClient(ip_buffer_index, bitstream_buffer_id,
-                     std::move(vda_buffer));
+                     std::move(vda_buffer), std::move(frame));
   // Flush or resolution change may be waiting image processor to finish.
   if (buffers_at_ip_.empty()) {
     NotifyFlushDoneIfNeeded();
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.h b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
index ab740e5..6a22d9c 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
@@ -13,6 +13,7 @@
 #include <stdint.h>
 
 #include <list>
+#include <map>
 #include <memory>
 #include <queue>
 #include <utility>
@@ -394,9 +395,15 @@
   // output buffer is |output_buffer_index| and its id is |bitstream_buffer_id|.
   bool ProcessFrame(int32_t bitstream_buffer_id, V4L2ReadableBufferRef buf);
 
+  // Send a buffer to the client.
+  // |buffer_index| is the output buffer index of the buffer to be sent.
+  // |bitstream_buffer_id| is the bitstream ID from which the buffer results.
+  // |vda_buffer| is the output VDA buffer containing the decoded frame.
+  // |frame| is the IP frame that will be sent to the client, if IP is used.
   void SendBufferToClient(size_t buffer_index,
                           int32_t bitstream_buffer_id,
-                          V4L2ReadableBufferRef vda_buffer);
+                          V4L2ReadableBufferRef vda_buffer,
+                          scoped_refptr<VideoFrame> frame = nullptr);
 
   //
   // Methods run on child thread.
@@ -518,10 +525,14 @@
   // Bitstream IDs and VDA buffers currently being processed by the IP.
   std::queue<std::pair<int32_t, V4L2ReadableBufferRef>> buffers_at_ip_;
   // Keeps decoded buffers out of the free list until the client returns them.
-  std::map<int32_t, V4L2ReadableBufferRef> buffers_at_client_;
+  // First element is the VDA buffer, second is the (optional) IP buffer.
+  std::map<int32_t, std::pair<V4L2ReadableBufferRef, scoped_refptr<VideoFrame>>>
+      buffers_at_client_;
   // Queue of buffers that have been returned by the client, but which fence
-  // hasn't been signaled yet.
-  std::queue<std::pair<std::unique_ptr<gl::GLFenceEGL>, V4L2ReadableBufferRef>>
+  // hasn't been signaled yet. Keeps both the VDA and (optional) IP buffer.
+  std::queue<
+      std::pair<std::unique_ptr<gl::GLFenceEGL>,
+                std::pair<V4L2ReadableBufferRef, scoped_refptr<VideoFrame>>>>
       buffers_awaiting_fence_;
 
   // Mapping of int index to output buffer record.
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index 65032498..90741a4 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -7,6 +7,7 @@
 #include "media/gpu/test/video_frame_file_writer.h"
 #include "media/gpu/test/video_frame_validator.h"
 #include "media/gpu/test/video_player/frame_renderer_dummy.h"
+#include "media/gpu/test/video_player/frame_renderer_thumbnail.h"
 #include "media/gpu/test/video_player/video.h"
 #include "media/gpu/test/video_player/video_collection.h"
 #include "media/gpu/test/video_player/video_decoder_client.h"
@@ -41,18 +42,20 @@
  public:
   std::unique_ptr<VideoPlayer> CreateVideoPlayer(
       const Video* video,
-      const VideoDecoderClientConfig& config = VideoDecoderClientConfig()) {
+      const VideoDecoderClientConfig& config = VideoDecoderClientConfig(),
+      std::unique_ptr<FrameRenderer> frame_renderer =
+          FrameRendererDummy::Create()) {
     LOG_ASSERT(video);
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors;
 
     // Validate decoded video frames.
-    if (g_env->output_frames_) {
+    if (g_env->enable_validator_) {
       frame_processors.push_back(
           media::test::VideoFrameValidator::Create(video->FrameChecksums()));
     }
 
     // Write decoded video frames to the 'video_frames/<test_name/>' folder.
-    if (g_env->enable_validator_) {
+    if (g_env->output_frames_) {
       const ::testing::TestInfo* const test_info =
           ::testing::UnitTest::GetInstance()->current_test_info();
       base::FilePath output_folder =
@@ -61,7 +64,7 @@
       frame_processors.push_back(VideoFrameFileWriter::Create(output_folder));
     }
 
-    return VideoPlayer::Create(video, FrameRendererDummy::Create(),
+    return VideoPlayer::Create(video, std::move(frame_renderer),
                                std::move(frame_processors), config);
   }
 };
@@ -253,6 +256,32 @@
   }
 }
 
+// Play a video from start to finish. Thumbnails of the decoded frames will be
+// rendered into a image, whose checksum is compared to a golden value. This
+// test is only needed on older platforms that don't support the video frame
+// validator, which requires direct access to the video frame's memory. This
+// test is only ran when --disable_validator is specified, and will be
+// deprecated in the future.
+TEST_F(VideoDecoderTest, FlushAtEndOfStream_RenderThumbnails) {
+  if (g_env->enable_validator_)
+    GTEST_SKIP();
+
+  VideoDecoderClientConfig config;
+  config.allocation_mode = AllocationMode::kAllocate;
+  auto tvp = CreateVideoPlayer(
+      g_env->video_, config,
+      FrameRendererThumbnail::Create(g_env->video_->FilePath()));
+
+  tvp->Play();
+  EXPECT_TRUE(tvp->WaitForFlushDone());
+
+  EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
+  EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
+  EXPECT_TRUE(tvp->WaitForFrameProcessors());
+  EXPECT_TRUE(static_cast<FrameRendererThumbnail*>(tvp->GetFrameRenderer())
+                  ->ValidateThumbnail());
+}
+
 }  // namespace test
 }  // namespace media
 
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index 563a35c..1f1ac59 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -25,6 +25,7 @@
 #include "components/viz/client/client_resource_provider.h"
 #include "components/viz/client/shared_bitmap_reporter.h"
 #include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/gpu/raster_context_provider.h"
 #include "components/viz/common/quads/render_pass.h"
 #include "components/viz/common/quads/stream_video_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
@@ -332,11 +333,16 @@
                         viz::ResourceFormat format,
                         const gfx::ColorSpace& color_space,
                         bool use_gpu_memory_buffer_resources,
-                        viz::ContextProvider* context_provider)
+                        viz::ContextProvider* context_provider,
+                        viz::RasterContextProvider* raster_context_provider)
       : PlaneResource(plane_resource_id, size, format, /*is_software=*/false),
-        context_provider_(context_provider) {
-    DCHECK(context_provider_);
-    const gpu::Capabilities& caps = context_provider_->ContextCapabilities();
+        context_provider_(context_provider),
+        raster_context_provider_(raster_context_provider) {
+    DCHECK(context_provider_ || raster_context_provider_);
+    const gpu::Capabilities& caps =
+        raster_context_provider_
+            ? raster_context_provider_->ContextCapabilities()
+            : context_provider_->ContextCapabilities();
     overlay_candidate_ = use_gpu_memory_buffer_resources &&
                          caps.texture_storage_image &&
                          IsGpuMemoryBufferFormatSupported(format);
@@ -347,24 +353,17 @@
       texture_target_ = gpu::GetBufferTextureTarget(gfx::BufferUsage::SCANOUT,
                                                     BufferFormat(format), caps);
     }
-    auto* sii = context_provider_->SharedImageInterface();
-    DCHECK(sii);
-    auto* gl = context_provider_->ContextGL();
-    DCHECK(gl);
-
+    auto* sii = SharedImageInterface();
     mailbox_ =
         sii->CreateSharedImage(format, size, color_space, shared_image_usage);
-    gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
+    ContextGL()->WaitSyncTokenCHROMIUM(
+        sii->GenUnverifiedSyncToken().GetConstData());
   }
 
   ~HardwarePlaneResource() override {
-    auto* sii = context_provider_->SharedImageInterface();
-    DCHECK(sii);
-    auto* gl = context_provider_->ContextGL();
-    DCHECK(gl);
     gpu::SyncToken sync_token;
-    gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
-    sii->DestroySharedImage(sync_token, mailbox_);
+    ContextGL()->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+    SharedImageInterface()->DestroySharedImage(sync_token, mailbox_);
   }
 
   const gpu::Mailbox& mailbox() const { return mailbox_; }
@@ -373,7 +372,23 @@
   bool overlay_candidate() const { return overlay_candidate_; }
 
  private:
+  gpu::SharedImageInterface* SharedImageInterface() {
+    auto* sii = raster_context_provider_
+                    ? raster_context_provider_->SharedImageInterface()
+                    : context_provider_->SharedImageInterface();
+    DCHECK(sii);
+    return sii;
+  }
+
+  gpu::gles2::GLES2Interface* ContextGL() {
+    auto* gl = raster_context_provider_ ? raster_context_provider_->ContextGL()
+                                        : context_provider_->ContextGL();
+    DCHECK(gl);
+    return gl;
+  }
+
   viz::ContextProvider* const context_provider_;
+  viz::RasterContextProvider* const raster_context_provider_;
   gpu::Mailbox mailbox_;
   GLenum texture_target_ = GL_TEXTURE_2D;
   bool overlay_candidate_ = false;
@@ -395,6 +410,7 @@
 
 VideoResourceUpdater::VideoResourceUpdater(
     viz::ContextProvider* context_provider,
+    viz::RasterContextProvider* raster_context_provider,
     viz::SharedBitmapReporter* shared_bitmap_reporter,
     viz::ClientResourceProvider* resource_provider,
     bool use_stream_video_draw_quad,
@@ -402,6 +418,7 @@
     bool use_r16_texture,
     int max_resource_size)
     : context_provider_(context_provider),
+      raster_context_provider_(raster_context_provider),
       shared_bitmap_reporter_(shared_bitmap_reporter),
       resource_provider_(resource_provider),
       use_stream_video_draw_quad_(use_stream_video_draw_quad),
@@ -410,7 +427,8 @@
       max_resource_size_(max_resource_size),
       tracing_id_(g_next_video_resource_updater_id.GetNext()),
       weak_ptr_factory_(this) {
-  DCHECK(context_provider_ || shared_bitmap_reporter_);
+  DCHECK(context_provider_ || raster_context_provider_ ||
+         shared_bitmap_reporter_);
 
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       this, "media::VideoResourceUpdater", base::ThreadTaskRunnerHandle::Get());
@@ -619,8 +637,10 @@
 
 viz::ResourceFormat VideoResourceUpdater::YuvResourceFormat(
     int bits_per_channel) {
-  DCHECK(context_provider_);
-  const auto& caps = context_provider_->ContextCapabilities();
+  DCHECK(raster_context_provider_ || context_provider_);
+  const auto& caps = raster_context_provider_
+                         ? raster_context_provider_->ContextCapabilities()
+                         : context_provider_->ContextCapabilities();
   if (caps.disable_one_component_textures)
     return viz::RGBA_8888;
   if (bits_per_channel <= 8)
@@ -684,7 +704,8 @@
   } else {
     all_resources_.push_back(std::make_unique<HardwarePlaneResource>(
         plane_resource_id, plane_size, format, color_space,
-        use_gpu_memory_buffer_resources_, context_provider_));
+        use_gpu_memory_buffer_resources_, context_provider_,
+        raster_context_provider_));
   }
   return all_resources_.back().get();
 }
@@ -711,7 +732,8 @@
   DCHECK_EQ(hardware_resource->texture_target(),
             static_cast<GLenum>(GL_TEXTURE_2D));
 
-  gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
+  auto* gl = raster_context_provider_ ? raster_context_provider_->ContextGL()
+                                      : context_provider_->ContextGL();
 
   gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData());
   // TODO(piman): convert to CreateAndTexStorage2DSharedImageCHROMIUM once
@@ -746,7 +768,7 @@
     scoped_refptr<VideoFrame> video_frame) {
   TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForHardwarePlanes");
   DCHECK(video_frame->HasTextures());
-  if (!context_provider_)
+  if (!context_provider_ && !raster_context_provider_)
     return VideoFrameExternalResources();
 
   VideoFrameExternalResources external_resources;
@@ -944,7 +966,9 @@
             video_frame.get(), upload_pixels_.get(), bytes_per_row);
 
         // Copy pixels into texture.
-        auto* gl = context_provider_->ContextGL();
+        auto* gl = raster_context_provider_
+                       ? raster_context_provider_->ContextGL()
+                       : context_provider_->ContextGL();
 
         const gfx::Size& plane_size = hardware_resource->resource_size();
         {
@@ -972,7 +996,10 @@
       HardwarePlaneResource* hardware_resource = plane_resource->AsHardware();
       external_resources.type = VideoFrameResourceType::RGBA;
       gpu::SyncToken sync_token;
-      GenerateCompositorSyncToken(context_provider_->ContextGL(), &sync_token);
+      auto* gl = raster_context_provider_
+                     ? raster_context_provider_->ContextGL()
+                     : context_provider_->ContextGL();
+      GenerateCompositorSyncToken(gl, &sync_token);
       transferable_resource = viz::TransferableResource::MakeGLOverlay(
           hardware_resource->mailbox(), GL_LINEAR,
           hardware_resource->texture_target(), sync_token,
@@ -1103,7 +1130,8 @@
 
     // Copy pixels into texture. TexSubImage2D() is applicable because
     // |yuv_resource_format| is LUMINANCE_F16, R16_EXT, LUMINANCE_8 or RED_8.
-    auto* gl = context_provider_->ContextGL();
+    auto* gl = raster_context_provider_ ? raster_context_provider_->ContextGL()
+                                        : context_provider_->ContextGL();
     DCHECK(GLSupportsFormat(plane_resource_format));
     {
       HardwarePlaneResource::ScopedTexture scope(gl, plane_resource);
@@ -1120,7 +1148,9 @@
 
   // Set the sync token otherwise resource is assumed to be synchronized.
   gpu::SyncToken sync_token;
-  GenerateCompositorSyncToken(context_provider_->ContextGL(), &sync_token);
+  auto* gl = raster_context_provider_ ? raster_context_provider_->ContextGL()
+                                      : context_provider_->ContextGL();
+  GenerateCompositorSyncToken(gl, &sync_token);
 
   for (size_t i = 0; i < plane_resources.size(); ++i) {
     HardwarePlaneResource* plane_resource = plane_resources[i]->AsHardware();
@@ -1149,7 +1179,9 @@
     return;
 
   // The video frame will insert a wait on the previous release sync token.
-  SyncTokenClientImpl client(context_provider_->ContextGL(), sync_token);
+  auto* gl = raster_context_provider_ ? raster_context_provider_->ContextGL()
+                                      : context_provider_->ContextGL();
+  SyncTokenClientImpl client(gl, sync_token);
   video_frame->UpdateReleaseSyncToken(&client);
 }
 
@@ -1166,8 +1198,9 @@
     return;
 
   if (context_provider_ && sync_token.HasData()) {
-    context_provider_->ContextGL()->WaitSyncTokenCHROMIUM(
-        sync_token.GetConstData());
+    auto* gl = raster_context_provider_ ? raster_context_provider_->ContextGL()
+                                        : context_provider_->ContextGL();
+    gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
   }
 
   if (lost_resource) {
diff --git a/media/renderers/video_resource_updater.h b/media/renderers/video_resource_updater.h
index 795a42f..39c3ed8 100644
--- a/media/renderers/video_resource_updater.h
+++ b/media/renderers/video_resource_updater.h
@@ -33,6 +33,7 @@
 namespace viz {
 class ClientResourceProvider;
 class ContextProvider;
+class RasterContextProvider;
 class RenderPass;
 class SharedBitmapReporter;
 }  // namespace viz
@@ -79,6 +80,7 @@
   // compositing |shared_bitmap_reporter| should be provided. If there is a
   // non-null |context_provider| we assume GPU compositing.
   VideoResourceUpdater(viz::ContextProvider* context_provider,
+                       viz::RasterContextProvider* raster_context_provider,
                        viz::SharedBitmapReporter* shared_bitmap_reporter,
                        viz::ClientResourceProvider* resource_provider,
                        bool use_stream_video_draw_quad,
@@ -131,7 +133,9 @@
     gfx::Size size_in_pixels;
   };
 
-  bool software_compositor() const { return context_provider_ == nullptr; }
+  bool software_compositor() const {
+    return context_provider_ == nullptr && raster_context_provider_ == nullptr;
+  }
 
   // Obtain a resource of the right format by either recycling an
   // unreferenced but appropriately formatted resource, or by
@@ -183,6 +187,7 @@
                     base::trace_event::ProcessMemoryDump* pmd) override;
 
   viz::ContextProvider* const context_provider_;
+  viz::RasterContextProvider* const raster_context_provider_;
   viz::SharedBitmapReporter* const shared_bitmap_reporter_;
   viz::ClientResourceProvider* const resource_provider_;
   const bool use_stream_video_draw_quad_;
diff --git a/media/renderers/video_resource_updater_unittest.cc b/media/renderers/video_resource_updater_unittest.cc
index ff8b426..fb39b4d 100644
--- a/media/renderers/video_resource_updater_unittest.cc
+++ b/media/renderers/video_resource_updater_unittest.cc
@@ -86,14 +86,16 @@
   std::unique_ptr<VideoResourceUpdater> CreateUpdaterForHardware(
       bool use_stream_video_draw_quad = false) {
     return std::make_unique<VideoResourceUpdater>(
-        context_provider_.get(), nullptr, resource_provider_.get(),
-        use_stream_video_draw_quad, /*use_gpu_memory_buffer_resources=*/false,
+        context_provider_.get(), /*raster_context_provider=*/nullptr, nullptr,
+        resource_provider_.get(), use_stream_video_draw_quad,
+        /*use_gpu_memory_buffer_resources=*/false,
         /*use_r16_texture=*/use_r16_texture_, /*max_resource_size=*/10000);
   }
 
   std::unique_ptr<VideoResourceUpdater> CreateUpdaterForSoftware() {
     return std::make_unique<VideoResourceUpdater>(
-        nullptr, &shared_bitmap_reporter_, resource_provider_.get(),
+        /*context_provider=*/nullptr, /*raster_context_provider=*/nullptr,
+        &shared_bitmap_reporter_, resource_provider_.get(),
         /*use_stream_video_draw_quad=*/false,
         /*use_gpu_memory_buffer_resources=*/false,
         /*use_r16_texture=*/false,
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 8e1afa87..d6b1d9d 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -3620,7 +3620,7 @@
   if (!annot)
     return false;
 
-  int flags = FPDFAnnot_GetFormFieldFlags(page, annot.get());
+  int flags = FPDFAnnot_GetFormFieldFlags(form(), page, annot.get());
   return CheckIfEditableFormTextArea(flags, form_type);
 }
 
diff --git a/remoting/base/oauth_helper.cc b/remoting/base/oauth_helper.cc
index 0e83109..62f4066f 100644
--- a/remoting/base/oauth_helper.cc
+++ b/remoting/base/oauth_helper.cc
@@ -29,11 +29,6 @@
          "https://www.googleapis.com/auth/userinfo.email ";
 }
 
-std::string GetDefaultOauthRedirectUrl() {
-  return "https://chromoting-oauth.talkgadget.google.com/talkgadget/oauth/"
-         "chrome-remote-desktop/rel/kgngmbheleoaphbjbaiobfdepmghbfah";
-}
-
 std::string GetOauthStartUrl(const std::string& redirect_url) {
   return base::StringPrintf(
       "https://accounts.google.com/o/oauth2/auth"
diff --git a/remoting/base/oauth_helper.h b/remoting/base/oauth_helper.h
index c0d5ec77..c659e1a 100644
--- a/remoting/base/oauth_helper.h
+++ b/remoting/base/oauth_helper.h
@@ -12,9 +12,6 @@
 // Gets the OAuth scope of the host's refresh token.
 std::string GetOauthScope();
 
-// Gets the default redirect URL for the OAuth dance.
-std::string GetDefaultOauthRedirectUrl();
-
 // Gets a URL at which the OAuth dance starts.
 std::string GetOauthStartUrl(const std::string& redirect_url);
 
diff --git a/remoting/base/oauth_token_getter_impl.cc b/remoting/base/oauth_token_getter_impl.cc
index 9c8e3ed..f095ccbe 100644
--- a/remoting/base/oauth_token_getter_impl.cc
+++ b/remoting/base/oauth_token_getter_impl.cc
@@ -230,16 +230,10 @@
           ? google_apis::CLIENT_REMOTING_HOST
           : google_apis::CLIENT_REMOTING;
 
-  std::string redirect_uri;
-  if (intermediate_credentials_->oauth_redirect_uri.empty()) {
-    if (intermediate_credentials_->is_service_account) {
-      redirect_uri = "oob";
-    } else {
-      redirect_uri = GetDefaultOauthRedirectUrl();
-    }
-  } else {
-    redirect_uri = intermediate_credentials_->oauth_redirect_uri;
-  }
+  // For the case of fetching an OAuth token from a one-time-use code, the
+  // caller should provide a redirect URI.
+  std::string redirect_uri = intermediate_credentials_->oauth_redirect_uri;
+  DCHECK(!redirect_uri.empty());
 
   gaia::OAuthClientInfo client_info = {
       google_apis::GetOAuth2ClientID(oauth2_client),
diff --git a/services/identity/public/cpp/identity_manager_unittest.cc b/services/identity/public/cpp/identity_manager_unittest.cc
index d862c700..05e37e3 100644
--- a/services/identity/public/cpp/identity_manager_unittest.cc
+++ b/services/identity/public/cpp/identity_manager_unittest.cc
@@ -669,6 +669,8 @@
 // Test that the user signing in results in firing of the IdentityManager
 // observer callback and the IdentityManager's state being updated.
 TEST_F(IdentityManagerTest, PrimaryAccountInfoAfterSignin) {
+  signin_manager()->ForceSignOut();
+
   base::RunLoop run_loop;
   identity_manager_observer()->set_on_primary_account_set_callback(
       run_loop.QuitClosure());
@@ -694,6 +696,7 @@
 // Test that the user signing out results in firing of the IdentityManager
 // observer callback and the IdentityManager's state being updated.
 TEST_F(IdentityManagerTest, PrimaryAccountInfoAfterSigninAndSignout) {
+  signin_manager()->ForceSignOut();
   // First ensure that the user is signed in from the POV of the
   // IdentityManager.
   base::RunLoop run_loop;
@@ -729,6 +732,7 @@
 // Test that the primary account's ID remains tracked by the IdentityManager
 // after signing in even after having removed the account without signing out.
 TEST_F(IdentityManagerTest, PrimaryAccountInfoAfterSigninAndAccountRemoval) {
+  signin_manager()->ForceSignOut();
   // First ensure that the user is signed in from the POV of the
   // IdentityManager.
   base::RunLoop run_loop;
diff --git a/services/identity/public/cpp/identity_test_utils.cc b/services/identity/public/cpp/identity_test_utils.cc
index 8ea39df..41d88a03 100644
--- a/services/identity/public/cpp/identity_test_utils.cc
+++ b/services/identity/public/cpp/identity_test_utils.cc
@@ -176,11 +176,21 @@
 CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager,
                                   const std::string& email) {
   DCHECK(!identity_manager->HasPrimaryAccount());
-
   SigninManagerBase* signin_manager = identity_manager->GetSigninManager();
   DCHECK(!signin_manager->IsAuthenticated());
 
-  std::string gaia_id = GetTestGaiaIdForEmail(email);
+  AccountTrackerService* account_tracker_service =
+      identity_manager->GetAccountTrackerService();
+  AccountInfo account_info =
+      account_tracker_service->FindAccountInfoByEmail(email);
+  if (account_info.account_id.empty()) {
+    std::string gaia_id = GetTestGaiaIdForEmail(email);
+    account_tracker_service->SeedAccountInfo(gaia_id, email);
+    account_info = account_tracker_service->FindAccountInfoByEmail(email);
+  }
+
+  std::string gaia_id = account_info.gaia;
+  DCHECK(!gaia_id.empty());
 
 #if defined(OS_CHROMEOS)
   // ChromeOS has no real notion of signin, so just plumb the information
@@ -189,26 +199,9 @@
   identity_manager->SetPrimaryAccountSynchronously(gaia_id, email,
                                                    /*refresh_token=*/"");
 #else
-
-  base::RunLoop run_loop;
-  OneShotIdentityManagerObserver signin_observer(
-      identity_manager, run_loop.QuitClosure(),
-      IdentityManagerEvent::PRIMARY_ACCOUNT_SET);
-
   SigninManager* real_signin_manager =
       SigninManager::FromSigninManagerBase(signin_manager);
-  // Note: It's important to pass base::DoNothing() (rather than a null
-  // callback) to make this work with both SigninManager and FakeSigninManager.
-  // If we would pass a null callback, then SigninManager would call
-  // CompletePendingSignin directly, but FakeSigninManager never does that.
-  // Note: pass an empty string as the refresh token so that no refresh token is
-  // set.
-  real_signin_manager->StartSignInWithRefreshToken(
-      /*refresh_token=*/"", gaia_id, email,
-      /*oauth_fetched_callback=*/base::DoNothing());
-  real_signin_manager->CompletePendingSignin();
-
-  run_loop.Run();
+  real_signin_manager->OnExternalSigninCompleted(email);
 #endif
 
   DCHECK(signin_manager->IsAuthenticated());
@@ -303,6 +296,10 @@
   DCHECK(account_tracker_service);
   DCHECK(account_tracker_service->FindAccountInfoByEmail(email).IsEmpty());
 
+  // Wait until tokens are loaded, otherwise the account will be removed as soon
+  // as tokens finish loading.
+  WaitForLoadCredentialsToComplete(identity_manager);
+
   std::string gaia_id = GetTestGaiaIdForEmail(email);
   account_tracker_service->SeedAccountInfo(gaia_id, email);
 
diff --git a/services/identity/public/cpp/primary_account_mutator.h b/services/identity/public/cpp/primary_account_mutator.h
index c2ef177..322a27b 100644
--- a/services/identity/public/cpp/primary_account_mutator.h
+++ b/services/identity/public/cpp/primary_account_mutator.h
@@ -10,8 +10,6 @@
 #include "base/callback_forward.h"
 #include "components/signin/core/browser/signin_metrics.h"
 
-struct AccountInfo;
-
 namespace identity {
 
 // PrimaryAccountMutator is the interface to set and clear the primary account
@@ -72,37 +70,9 @@
   // and should not be used when writing new code. They will be removed when the
   // old sign-in workflow has been turned down.
 
-  // Attempts to sign-in user with a given refresh token and account. If it is
-  // defined, |callback| should invoke either ClearPrimaryAccount() or
-  // LegacyCompletePendingPrimaryAccountSignin() to either cancel or continue
-  // the in progress sign-in (legacy, pre-DICE workflow).
-  virtual void LegacyStartSigninWithRefreshTokenForPrimaryAccount(
-      const std::string& refresh_token,
-      const std::string& gaia_id,
-      const std::string& username,
-      base::OnceCallback<void(const std::string&)> callback) = 0;
-
-  // Complete the in-process sign-in (legacy, pre-DICE workflow).
-  virtual void LegacyCompletePendingPrimaryAccountSignin() = 0;
-
   // If applicable, merges the signed-in account into the cookie jar (legacy,
   // pre-DICE workflow).
   virtual void LegacyMergeSigninCredentialIntoCookieJar() = 0;
-
-  // Returns true if there is a sign-in in progress (legacy, pre-DICE workflow).
-  virtual bool LegacyIsPrimaryAccountAuthInProgress() const = 0;
-
-  // If an authentication is in progress, returns the AccountInfo for the
-  // account being authenticated. Returns an empty AccountInfo if no auth is
-  // in progress (legacy, pre-DICE workflow).
-  virtual AccountInfo LegacyPrimaryAccountForAuthInProgress() const = 0;
-
-  // Copy auth credentials from the other PrimaryAccountMutator to this one.
-  // Used when creating a new profile during the sign-in process to transfer
-  // the in-progress credential information to the new profile (legacy, pre-
-  // DICE workflow).
-  virtual void LegacyCopyCredentialsFrom(
-      const PrimaryAccountMutator& source) = 0;
 };
 
 }  // namespace identity
diff --git a/services/identity/public/cpp/primary_account_mutator_impl.cc b/services/identity/public/cpp/primary_account_mutator_impl.cc
index db9edf3..de9412b1 100644
--- a/services/identity/public/cpp/primary_account_mutator_impl.cc
+++ b/services/identity/public/cpp/primary_account_mutator_impl.cc
@@ -43,10 +43,7 @@
     ClearAccountsAction action,
     signin_metrics::ProfileSignout source_metric,
     signin_metrics::SignoutDelete delete_metric) {
-  // Check if and auth process is ongoing before reporting failure to support
-  // the legacy workflow of cancelling it by clearing the primary account.
-  if (!signin_manager_->IsAuthenticated() &&
-      !LegacyIsPrimaryAccountAuthInProgress())
+  if (!signin_manager_->IsAuthenticated())
     return false;
 
   switch (action) {
@@ -78,45 +75,8 @@
   NOTIMPLEMENTED();
 }
 
-void PrimaryAccountMutatorImpl::
-    LegacyStartSigninWithRefreshTokenForPrimaryAccount(
-        const std::string& refresh_token,
-        const std::string& gaia_id,
-        const std::string& username,
-        base::OnceCallback<void(const std::string&)> callback) {
-  signin_manager_->StartSignInWithRefreshToken(refresh_token, gaia_id, username,
-                                               std::move(callback));
-}
-
-void PrimaryAccountMutatorImpl::LegacyCompletePendingPrimaryAccountSignin() {
-  signin_manager_->CompletePendingSignin();
-}
-
 void PrimaryAccountMutatorImpl::LegacyMergeSigninCredentialIntoCookieJar() {
   signin_manager_->MergeSigninCredentialIntoCookieJar();
 }
 
-bool PrimaryAccountMutatorImpl::LegacyIsPrimaryAccountAuthInProgress() const {
-  return signin_manager_->AuthInProgress();
-}
-
-AccountInfo PrimaryAccountMutatorImpl::LegacyPrimaryAccountForAuthInProgress()
-    const {
-  if (!LegacyIsPrimaryAccountAuthInProgress())
-    return AccountInfo{};
-
-  AccountInfo account_info;
-  account_info.account_id = signin_manager_->GetAccountIdForAuthInProgress();
-  account_info.gaia = signin_manager_->GetGaiaIdForAuthInProgress();
-  account_info.email = signin_manager_->GetUsernameForAuthInProgress();
-
-  return account_info;
-}
-
-void PrimaryAccountMutatorImpl::LegacyCopyCredentialsFrom(
-    const PrimaryAccountMutator& source) {
-  signin_manager_->CopyCredentialsFrom(
-      *static_cast<const PrimaryAccountMutatorImpl&>(source).signin_manager_);
-}
-
 }  // namespace identity
diff --git a/services/identity/public/cpp/primary_account_mutator_impl.h b/services/identity/public/cpp/primary_account_mutator_impl.h
index 54de8cc7..7807fa1 100644
--- a/services/identity/public/cpp/primary_account_mutator_impl.h
+++ b/services/identity/public/cpp/primary_account_mutator_impl.h
@@ -29,16 +29,7 @@
   bool IsSettingPrimaryAccountAllowed() const override;
   void SetSettingPrimaryAccountAllowed(bool allowed) override;
   void SetAllowedPrimaryAccountPattern(const std::string& pattern) override;
-  void LegacyStartSigninWithRefreshTokenForPrimaryAccount(
-      const std::string& refresh_token,
-      const std::string& gaia_id,
-      const std::string& username,
-      base::OnceCallback<void(const std::string&)> callback) override;
-  void LegacyCompletePendingPrimaryAccountSignin() override;
   void LegacyMergeSigninCredentialIntoCookieJar() override;
-  bool LegacyIsPrimaryAccountAuthInProgress() const override;
-  AccountInfo LegacyPrimaryAccountForAuthInProgress() const override;
-  void LegacyCopyCredentialsFrom(const PrimaryAccountMutator& source) override;
 
  private:
   // Pointers to the services used by the PrimaryAccountMutatorImpl. They
diff --git a/services/identity/public/cpp/primary_account_mutator_unittest.cc b/services/identity/public/cpp/primary_account_mutator_unittest.cc
index 81ed22f..0937dda 100644
--- a/services/identity/public/cpp/primary_account_mutator_unittest.cc
+++ b/services/identity/public/cpp/primary_account_mutator_unittest.cc
@@ -20,7 +20,6 @@
 const char kUnknownAccountId[] = "{unknown account id}";
 const char kPrimaryAccountEmail[] = "primary.account@example.com";
 const char kAnotherAccountEmail[] = "another.account@example.com";
-const char kRefreshToken[] = "refresh_token";
 
 // All account consistency methods that are tested by those unit tests when
 // testing ClearPrimaryAccount method.
@@ -520,426 +519,3 @@
       identity::PrimaryAccountMutator::ClearAccountsAction::kDefault,
       RemoveAccountExpectation::kRemovePrimary, AuthExpectation::kAuthError);
 }
-
-// Test that ClearPrimaryAccount(...) with authentication in progress notifies
-// Observers that sign-in is canceled and does not remove any tokens.
-TEST_F(PrimaryAccountMutatorTest, ClearPrimaryAccount_AuthInProgress) {
-  base::test::ScopedTaskEnvironment task_environment;
-  identity::IdentityTestEnvironment environment;
-
-  identity::IdentityManager* identity_manager = environment.identity_manager();
-  identity::PrimaryAccountMutator* primary_account_mutator =
-      identity_manager->GetPrimaryAccountMutator();
-
-  // Abort the test if the current platform does not support mutation of the
-  // primary account (the returned PrimaryAccountMutator* will be null).
-  if (!primary_account_mutator)
-    return;
-
-  AccountInfo account_info =
-      environment.MakeAccountAvailable(kPrimaryAccountEmail);
-  EXPECT_TRUE(
-      identity_manager->HasAccountWithRefreshToken(account_info.account_id));
-
-  // Account available in the tracker service but still not authenticated means
-  // there's neither a primary account nor an authentication process ongoing.
-  EXPECT_FALSE(identity_manager->HasPrimaryAccount());
-  EXPECT_FALSE(primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-
-  // Add a secondary account to verify that its refresh token survives the
-  // call to ClearPrimaryAccount(...) below.
-  AccountInfo secondary_account_info =
-      MakeAccountAvailable(identity_manager, kAnotherAccountEmail);
-  EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken(
-      secondary_account_info.account_id));
-
-  // Start a signin process for the account we just made available and check
-  // that it's reported to be in progress before the process is completed.
-  base::RunLoop run_loop;
-  std::string signed_account_refresh_token;
-  primary_account_mutator->LegacyStartSigninWithRefreshTokenForPrimaryAccount(
-      kRefreshToken, account_info.gaia, account_info.email,
-      base::BindOnce(
-          [](std::string* out_refresh_token, const std::string& refresh_token) {
-            *out_refresh_token = refresh_token;
-          },
-          base::Unretained(&signed_account_refresh_token)));
-
-  EXPECT_TRUE(primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-
-  AccountInfo auth_in_progress_account_info =
-      primary_account_mutator->LegacyPrimaryAccountForAuthInProgress();
-
-  // No primary account to "clear", so no callback.
-  PrimaryAccountClearedCallback primary_account_cleared_callback =
-      base::BindRepeating([](const CoreAccountInfo&) {
-        FAIL() << "no primary account is set, so nothing should be cleared";
-      });
-
-  // Capture the authentication error and make sure we exit the run loop.
-  GoogleServiceAuthError captured_auth_error;
-  PrimaryAccountSigninFailedCallback primary_account_signin_failed_callback =
-      base::BindRepeating(
-          [](base::RepeatingClosure quit_closure,
-             GoogleServiceAuthError* out_auth_error,
-             const GoogleServiceAuthError& auth_error) {
-            *out_auth_error = auth_error;
-            quit_closure.Run();
-          },
-          run_loop.QuitClosure(), &captured_auth_error);
-
-  // Observer should not be notified of any token removals.
-  RefreshTokenRemovedCallback refresh_token_removed_callback =
-      base::BindRepeating([](const std::string& removed_account) {
-        FAIL() << "no token removal should happen";
-      });
-
-  ClearPrimaryAccountTestObserver scoped_observer(
-      identity_manager, primary_account_cleared_callback,
-      primary_account_signin_failed_callback, refresh_token_removed_callback);
-
-  EXPECT_TRUE(primary_account_mutator->ClearPrimaryAccount(
-      identity::PrimaryAccountMutator::ClearAccountsAction::kRemoveAll,
-      signin_metrics::SIGNOUT_TEST,
-      signin_metrics::SignoutDelete::IGNORE_METRIC));
-  run_loop.Run();
-
-  // Verify in-progress authentication was canceled.
-  EXPECT_EQ(captured_auth_error.state(),
-            GoogleServiceAuthError::State::REQUEST_CANCELED);
-  EXPECT_FALSE(primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-
-  // We didn't have a primary account to start with, we shouldn't have one now
-  // either.
-  EXPECT_FALSE(identity_manager->HasPrimaryAccount());
-  EXPECT_FALSE(identity_manager->HasPrimaryAccountWithRefreshToken());
-
-  // Secondary account token still exists.
-  EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken(
-      secondary_account_info.account_id));
-}
-
-// Checks that checking whether an authentication process is in progress reports
-// true before starting and after successfully completing the signin process.
-TEST_F(PrimaryAccountMutatorTest, SigninWithRefreshToken) {
-  base::test::ScopedTaskEnvironment task_environment;
-  identity::IdentityTestEnvironment environment;
-
-  identity::IdentityManager* identity_manager = environment.identity_manager();
-  identity::PrimaryAccountMutator* primary_account_mutator =
-      identity_manager->GetPrimaryAccountMutator();
-
-  // Abort the test if the current platform does not support mutation of the
-  // primary account (the returned PrimaryAccountMutator* will be null).
-  if (!primary_account_mutator)
-    return;
-
-  // We'll sign in the same account twice, using SetPrimaryAccount() and
-  // LegacyStartSigninWithRefreshTokenForPrimaryAccount(), and check that
-  // in both cases the end result is the same.
-  AccountInfo account_info =
-      environment.MakeAccountAvailable(kPrimaryAccountEmail);
-
-  EXPECT_FALSE(identity_manager->HasPrimaryAccount());
-  EXPECT_TRUE(
-      primary_account_mutator->SetPrimaryAccount(account_info.account_id));
-
-  const std::string primary_account_id_1 =
-      identity_manager->GetPrimaryAccountId();
-
-  EXPECT_TRUE(identity_manager->HasPrimaryAccount());
-  EXPECT_FALSE(primary_account_id_1.empty());
-  EXPECT_EQ(primary_account_id_1, account_info.account_id);
-
-  EXPECT_TRUE(
-      identity_manager->HasAccountWithRefreshToken(primary_account_id_1));
-  EXPECT_FALSE(identity_manager->GetPrimaryAccountInfo().email.empty());
-
-  // Sign out the account to try to sign in again with the other mechanism, but
-  // using kKeepAll so we can use the same account we made available before.
-  EXPECT_TRUE(primary_account_mutator->ClearPrimaryAccount(
-      identity::PrimaryAccountMutator::ClearAccountsAction::kKeepAll,
-      signin_metrics::SIGNOUT_TEST,
-      signin_metrics::SignoutDelete::IGNORE_METRIC));
-
-  EXPECT_FALSE(identity_manager->HasPrimaryAccount());
-  EXPECT_TRUE(identity_manager->GetPrimaryAccountId().empty());
-  EXPECT_TRUE(identity_manager->GetPrimaryAccountInfo().email.empty());
-
-  // Start a signin process for the account and complete it right away to check
-  // whether we end up with a similar result than with SetPrimaryAccount().
-  std::string signed_account_refresh_token;
-  primary_account_mutator->LegacyStartSigninWithRefreshTokenForPrimaryAccount(
-      kRefreshToken, account_info.gaia, account_info.email,
-      base::BindOnce(
-          [](std::string* out_refresh_token, const std::string& refresh_token) {
-            *out_refresh_token = refresh_token;
-          },
-          base::Unretained(&signed_account_refresh_token)));
-
-  primary_account_mutator->LegacyCompletePendingPrimaryAccountSignin();
-
-  // The refresh token assigned to the account should match the one passed.
-  EXPECT_EQ(signed_account_refresh_token, kRefreshToken);
-
-  EXPECT_TRUE(identity_manager->HasPrimaryAccount());
-  EXPECT_FALSE(primary_account_id_1.empty());
-  EXPECT_EQ(primary_account_id_1, account_info.account_id);
-
-  EXPECT_TRUE(
-      identity_manager->HasAccountWithRefreshToken(primary_account_id_1));
-  EXPECT_FALSE(identity_manager->GetPrimaryAccountInfo().email.empty());
-
-  const std::string primary_account_id_2 =
-      identity_manager->GetPrimaryAccountId();
-
-  EXPECT_TRUE(identity_manager->HasPrimaryAccount());
-  EXPECT_FALSE(primary_account_id_2.empty());
-  EXPECT_EQ(primary_account_id_2, account_info.account_id);
-
-  EXPECT_TRUE(
-      identity_manager->HasAccountWithRefreshToken(primary_account_id_2));
-  EXPECT_FALSE(identity_manager->GetPrimaryAccountInfo().email.empty());
-
-  // Information retrieved via the IdentityManager now for the current primary
-  // account should match the data of the account we signed in before.
-  EXPECT_EQ(primary_account_id_1, primary_account_id_2);
-}
-
-// Checks that checking whether an authentication process is in progress reports
-// true before starting and after successfully completing the signin process.
-TEST_F(PrimaryAccountMutatorTest, AuthInProgress_SigninCompleted) {
-  base::test::ScopedTaskEnvironment task_environment;
-  identity::IdentityTestEnvironment environment;
-
-  identity::IdentityManager* identity_manager = environment.identity_manager();
-  identity::PrimaryAccountMutator* primary_account_mutator =
-      identity_manager->GetPrimaryAccountMutator();
-
-  // Abort the test if the current platform does not support mutation of the
-  // primary account (the returned PrimaryAccountMutator* will be null).
-  if (!primary_account_mutator)
-    return;
-
-  AccountInfo account_info =
-      environment.MakeAccountAvailable(kPrimaryAccountEmail);
-
-  // Account available in the tracker service but still not authenticated means
-  // there's neither a primary account nor an authentication process ongoing.
-  EXPECT_FALSE(identity_manager->HasPrimaryAccount());
-  EXPECT_FALSE(primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-
-  // Start a signin process for the account we just made available and check
-  // that it's reported to be in progress before the process is completed.
-  base::RunLoop run_loop;
-  std::string signed_account_refresh_token;
-  primary_account_mutator->LegacyStartSigninWithRefreshTokenForPrimaryAccount(
-      kRefreshToken, account_info.gaia, account_info.email,
-      base::BindOnce(
-          [](std::string* out_refresh_token, const std::string& refresh_token) {
-            *out_refresh_token = refresh_token;
-          },
-          base::Unretained(&signed_account_refresh_token)));
-
-  EXPECT_TRUE(primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-
-  AccountInfo auth_in_progress_account_info =
-      primary_account_mutator->LegacyPrimaryAccountForAuthInProgress();
-
-  // The data from the AccountInfo related to the authentication process still
-  // in progress should match the data of the account being signed in.
-  EXPECT_EQ(auth_in_progress_account_info.account_id, account_info.account_id);
-  EXPECT_EQ(auth_in_progress_account_info.gaia, account_info.gaia);
-  EXPECT_EQ(auth_in_progress_account_info.email, account_info.email);
-
-  // Finally, complete the signin process so that we can do further checks.
-  primary_account_mutator->LegacyCompletePendingPrimaryAccountSignin();
-  run_loop.RunUntilIdle();
-
-  // The refresh token assigned to the account should match the one passed.
-  EXPECT_EQ(signed_account_refresh_token, kRefreshToken);
-
-  // An account has been authenticated now, so there should be a primary account
-  // authenticated and no authentication process reported as in progress now.
-  EXPECT_TRUE(identity_manager->HasPrimaryAccount());
-  EXPECT_FALSE(primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-
-  // Information retrieved via the IdentityManager now for the current primary
-  // account should match the data of the account being signed in.
-  EXPECT_EQ(identity_manager->GetPrimaryAccountId(), account_info.account_id);
-  AccountInfo identity_manager_account_info =
-      identity_manager->GetPrimaryAccountInfo();
-  EXPECT_EQ(identity_manager_account_info.account_id, account_info.account_id);
-  EXPECT_EQ(identity_manager_account_info.gaia, account_info.gaia);
-  EXPECT_EQ(identity_manager_account_info.email, account_info.email);
-}
-
-// Checks that checking whether an authentication process is in progress reports
-// true before starting and after cancelling and ongoing signin process.
-TEST_F(PrimaryAccountMutatorTest, AuthInProgress_SigninCancelled) {
-  base::test::ScopedTaskEnvironment task_environment;
-  identity::IdentityTestEnvironment environment;
-
-  identity::IdentityManager* identity_manager = environment.identity_manager();
-  identity::PrimaryAccountMutator* primary_account_mutator =
-      identity_manager->GetPrimaryAccountMutator();
-
-  // Abort the test if the current platform does not support mutation of the
-  // primary account (the returned PrimaryAccountMutator* will be null).
-  if (!primary_account_mutator)
-    return;
-
-  AccountInfo account_info =
-      environment.MakeAccountAvailable(kPrimaryAccountEmail);
-
-  // Account available in the tracker service but still not authenticated means
-  // there's neither a primary account nor an authentication process ongoing.
-  EXPECT_FALSE(identity_manager->HasPrimaryAccount());
-  EXPECT_FALSE(primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-
-  // Start a signin process for the account we just made available and check
-  // that it's reported to be in progress before the process is completed.
-  base::RunLoop run_loop;
-  std::string signed_account_refresh_token;
-  primary_account_mutator->LegacyStartSigninWithRefreshTokenForPrimaryAccount(
-      kRefreshToken, account_info.gaia, account_info.email,
-      base::BindOnce(
-          [](std::string* out_refresh_token, const std::string& refresh_token) {
-            *out_refresh_token = refresh_token;
-          },
-          base::Unretained(&signed_account_refresh_token)));
-
-  EXPECT_TRUE(primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-
-  AccountInfo auth_in_progress_account_info =
-      primary_account_mutator->LegacyPrimaryAccountForAuthInProgress();
-
-  // The data from the AccountInfo related to the authentication process still
-  // in progress should match the data of the account being signed in.
-  EXPECT_EQ(auth_in_progress_account_info.account_id, account_info.account_id);
-  EXPECT_EQ(auth_in_progress_account_info.gaia, account_info.gaia);
-  EXPECT_EQ(auth_in_progress_account_info.email, account_info.email);
-
-  // Now cancel the signin process (by attempting to clear the primary account
-  // we were trying to sign in so far), so that we can do further checks.
-  EXPECT_TRUE(primary_account_mutator->ClearPrimaryAccount(
-      identity::PrimaryAccountMutator::ClearAccountsAction::kRemoveAll,
-      signin_metrics::SIGNOUT_TEST,
-      signin_metrics::SignoutDelete::IGNORE_METRIC));
-  run_loop.RunUntilIdle();
-
-  // The refresh token assigned to the account should match the one passed.
-  EXPECT_EQ(signed_account_refresh_token, kRefreshToken);
-
-  // The request has been cancelled, so there should not be a primary account
-  // signed in, the refresh we just received should not be valid for the primary
-  // account (even if it's been fetched and stored for the account already) and
-  // no authentication process reported as in progress now.
-  EXPECT_FALSE(identity_manager->HasPrimaryAccount());
-  EXPECT_FALSE(identity_manager->HasPrimaryAccountWithRefreshToken());
-  EXPECT_TRUE(
-      identity_manager->HasAccountWithRefreshToken(account_info.account_id));
-  EXPECT_FALSE(primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-
-  // Information retrieved via the IdentityManager confirms the cancelation.
-  EXPECT_EQ(identity_manager->GetPrimaryAccountId(), std::string());
-  EXPECT_TRUE(identity_manager->GetPrimaryAccountInfo().IsEmpty());
-}
-
-// Checks that copying the credentials from another PrimaryAccountMutator works.
-TEST_F(PrimaryAccountMutatorTest, CopyCredentialsFrom) {
-  base::test::ScopedTaskEnvironment task_environment;
-  identity::IdentityTestEnvironment environment;
-
-  identity::IdentityManager* identity_manager = environment.identity_manager();
-  identity::PrimaryAccountMutator* primary_account_mutator =
-      identity_manager->GetPrimaryAccountMutator();
-
-  // Abort the test if the current platform does not support mutation of the
-  // primary account (the returned PrimaryAccountMutator* will be null).
-  if (!primary_account_mutator)
-    return;
-
-  // We will need another PrimaryAccountMutator to copy the credentials from the
-  // one used previously and check that they match later on.
-  identity::IdentityTestEnvironment other_environment;
-  identity::IdentityManager* other_identity_manager =
-      other_environment.identity_manager();
-  identity::PrimaryAccountMutator* other_primary_account_mutator =
-      other_identity_manager->GetPrimaryAccountMutator();
-
-  AccountInfo account_info =
-      environment.MakeAccountAvailable(kPrimaryAccountEmail);
-
-  // Start a signin process for the account we just made available so that we
-  // can check whether the credentials copied to another PrimaryAccountMutator.
-  base::RunLoop run_loop;
-  std::string signed_account_refresh_token;
-  primary_account_mutator->LegacyStartSigninWithRefreshTokenForPrimaryAccount(
-      kRefreshToken, account_info.gaia, account_info.email,
-      base::BindOnce(
-          [](std::string* out_refresh_token, const std::string& refresh_token) {
-            *out_refresh_token = refresh_token;
-          },
-          base::Unretained(&signed_account_refresh_token)));
-  run_loop.RunUntilIdle();
-
-  // The refresh token assigned to the account should match the one passed.
-  EXPECT_EQ(signed_account_refresh_token, kRefreshToken);
-
-  // This is a good moment to copy the credentials from one mutator to the other
-  // since internal transient data hold by the SigninManager in this state will
-  // be non-empty while the authentication process is ongoing (e.g. possibly
-  // invalid account ID, Gaia ID and email), allowing us to compare values.
-  base::RunLoop run_loop2;
-  other_primary_account_mutator->LegacyCopyCredentialsFrom(
-      *primary_account_mutator);
-  run_loop2.RunUntilIdle();
-
-  EXPECT_TRUE(primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-  EXPECT_TRUE(
-      other_primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-
-  AccountInfo auth_in_progress_account_info =
-      primary_account_mutator->LegacyPrimaryAccountForAuthInProgress();
-  AccountInfo other_auth_in_progress_account_info =
-      other_primary_account_mutator->LegacyPrimaryAccountForAuthInProgress();
-
-  EXPECT_FALSE(auth_in_progress_account_info.IsEmpty());
-  EXPECT_FALSE(other_auth_in_progress_account_info.IsEmpty());
-
-  EXPECT_EQ(auth_in_progress_account_info.account_id,
-            other_auth_in_progress_account_info.account_id);
-  EXPECT_EQ(auth_in_progress_account_info.gaia,
-            other_auth_in_progress_account_info.gaia);
-  EXPECT_EQ(auth_in_progress_account_info.email,
-            other_auth_in_progress_account_info.email);
-
-  // Finally, complete the signin process so that we can do further checks.
-  base::RunLoop run_loop3;
-  primary_account_mutator->LegacyCompletePendingPrimaryAccountSignin();
-  run_loop3.RunUntilIdle();
-
-  // An account has been authenticated now, so there should be a primary account
-  // authenticated and no authentication process reported as in progress now.
-  EXPECT_TRUE(identity_manager->HasPrimaryAccount());
-  EXPECT_FALSE(primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-
-  // Query again the information for each of the two different environments now
-  // that the original one has completed the authentication process and compare
-  // them one more time: they should not match as the original one is no longer
-  // in the middle of the authentication process.
-  EXPECT_TRUE(identity_manager->HasPrimaryAccount());
-  EXPECT_FALSE(other_identity_manager->HasPrimaryAccount());
-
-  EXPECT_FALSE(primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-  EXPECT_TRUE(
-      other_primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress());
-
-  auth_in_progress_account_info =
-      primary_account_mutator->LegacyPrimaryAccountForAuthInProgress();
-  other_auth_in_progress_account_info =
-      other_primary_account_mutator->LegacyPrimaryAccountForAuthInProgress();
-  EXPECT_TRUE(auth_in_progress_account_info.IsEmpty());
-  EXPECT_FALSE(other_auth_in_progress_account_info.IsEmpty());
-}
diff --git a/services/image_annotation/annotator.cc b/services/image_annotation/annotator.cc
index 95b6969..9fd9d54 100644
--- a/services/image_annotation/annotator.cc
+++ b/services/image_annotation/annotator.cc
@@ -29,9 +29,9 @@
 // Returns nullopt if there is any unexpected structure to the annotations
 // message.
 base::Optional<std::string> ParseJsonOcrAnnotation(
-    const base::Value& annotations,
+    const base::Value& ocr_engine,
     const double min_ocr_confidence) {
-  const base::Value* const ocr_regions = annotations.FindKey("ocrRegions");
+  const base::Value* const ocr_regions = ocr_engine.FindKey("ocrRegions");
   // No OCR regions is valid - it just means there is no text.
   if (!ocr_regions)
     return std::string();
@@ -107,12 +107,21 @@
     if (!image_id || !image_id->is_string())
       continue;
 
-    const base::Value* const annotations = result.FindKey("annotations");
-    if (!annotations || !annotations->is_dict())
+    const base::Value* const engine_results = result.FindKey("engineResults");
+    if (!engine_results || !engine_results->is_list() ||
+        engine_results->GetList().size() != 1)
+      continue;
+
+    const base::Value& engine_result = engine_results->GetList()[0];
+    if (!engine_result.is_dict())
+      continue;
+
+    const base::Value* const ocr_engine = engine_result.FindKey("ocrEngine");
+    if (!ocr_engine || !ocr_engine->is_dict())
       continue;
 
     const base::Optional<std::string> ocr_text =
-        ParseJsonOcrAnnotation(*annotations, min_ocr_confidence);
+        ParseJsonOcrAnnotation(*ocr_engine, min_ocr_confidence);
     if (!ocr_text.has_value())
       continue;
 
@@ -199,22 +208,25 @@
                           it->second.size()),
         &base64_data);
 
+    // TODO(crbug.com/916420): accept and propagate page language info to
+    //                         improve OCR accuracy.
+    base::Value engine_params(base::Value::Type::DICTIONARY);
+    engine_params.SetKey("ocrParameters",
+                         base::Value(base::Value::Type::DICTIONARY));
+
+    base::Value engine_params_list(base::Value::Type::LIST);
+    engine_params_list.GetList().push_back(std::move(engine_params));
+
     base::Value image_request(base::Value::Type::DICTIONARY);
     image_request.SetKey("imageId", base::Value(it->first));
     image_request.SetKey("imageBytes", base::Value(std::move(base64_data)));
+    image_request.SetKey("engineParameters", std::move(engine_params_list));
 
     image_request_list.GetList().push_back(std::move(image_request));
   }
 
-  // TODO(crbug.com/916420): accept and propagate page language info to improve
-  //                         OCR accuracy.
-  base::Value feature_request(base::Value::Type::DICTIONARY);
-  feature_request.SetKey("ocrFeature",
-                         base::Value(base::Value::Type::DICTIONARY));
-
   base::Value request(base::Value::Type::DICTIONARY);
-  request.SetKey("imageRequests", base::Value(std::move(image_request_list)));
-  request.SetKey("featureRequest", std::move(feature_request));
+  request.SetKey("imageRequests", std::move(image_request_list));
 
   std::string json_request;
   base::JSONWriter::Write(request, &json_request);
diff --git a/services/image_annotation/annotator_unittest.cc b/services/image_annotation/annotator_unittest.cc
index dc642e1..790dba06 100644
--- a/services/image_annotation/annotator_unittest.cc
+++ b/services/image_annotation/annotator_unittest.cc
@@ -42,16 +42,13 @@
 // Template for a request for a single image.
 constexpr char kTemplateRequest[] = R"(
 {
-  "imageRequests": [
-    {
-      "imageId": "%s",
-      "imageBytes": "%s"
-    }
-  ],
-
-  "featureRequest": {
-    "ocrFeature": {}
-  }
+  "imageRequests": [{
+    "imageId": "%s",
+    "imageBytes": "%s",
+    "engineParameters": [{
+      "ocrParameters": {}
+    }]
+  }]
 }
 )";
 
@@ -61,29 +58,36 @@
   "imageRequests": [
     {
       "imageId": "https://www.example.com/image1.jpg",
-      "imageBytes": "AQID"
+      "imageBytes": "AQID",
+      "engineParameters": [{
+        "ocrParameters": {}
+      }]
     },
     {
       "imageId": "https://www.example.com/image2.jpg",
-      "imageBytes": "BAUG"
+      "imageBytes": "BAUG",
+      "engineParameters": [{
+        "ocrParameters": {}
+      }]
     },
     {
       "imageId": "https://www.example.com/image3.jpg",
-      "imageBytes": "BwgJ"
+      "imageBytes": "BwgJ",
+      "engineParameters": [{
+        "ocrParameters": {}
+      }]
     }
-  ],
-  "featureRequest": {
-    "ocrFeature": {}
-  }
+  ]
 })";
 
 // Successful text extraction for |kImage1Url|.
 constexpr char kSuccessResponse[] = R"(
-  {
-    "results": [
-      {
-        "imageId": "https://www.example.com/image1.jpg",
-        "annotations": {
+{
+  "results": [
+    {
+      "imageId": "https://www.example.com/image1.jpg",
+      "engineResults": [{
+        "ocrEngine": {
           "ocrRegions": [
             {
               "words": [
@@ -111,24 +115,25 @@
             }
           ]
         }
-      }
-    ]
-  }
+      }]
+    }
+  ]
+}
 )";
 
 // Failed text extraction for |kImage1Url|.
 constexpr char kErrorResponse[] = R"(
-  {
-    "results": [
-      {
-        "imageId": "https://www.example.com/image1.jpg",
-        "status": {
-          "code": 8,
-          "message": "Resource exhaused"
-        }
+{
+  "results": [{
+    "imageId": "https://www.example.com/image1.jpg",
+    "engineResults": [{
+      "status": {
+        "code": 8,
+        "message": "Resource exhaused"
       }
-    ]
-  }
+    }]
+  }]
+}
 )";
 
 // Batch response containing successful annotations for |kImage1Url| and
@@ -140,40 +145,38 @@
   "results": [
     {
       "imageId": "https://www.example.com/image2.jpg",
-      "annotations": {
-        "ocrRegions": [
-          {
-            "words": [
-               {
-                 "detectedText": "2",
-                 "confidenceScore": 1.0
-               }
-            ]
-          }
-        ]
-      }
+      "engineResults": [{
+        "ocrEngine": {
+          "ocrRegions": [{
+            "words": [{
+              "detectedText": "2",
+              "confidenceScore": 1.0
+            }]
+          }]
+        }
+      }]
     },
     {
       "imageId": "https://www.example.com/image1.jpg",
-      "annotations": {
-        "ocrRegions": [
-          {
-            "words": [
-               {
-                 "detectedText": "1",
-                 "confidenceScore": 1.0
-               }
-            ]
-          }
-        ]
-      }
+      "engineResults": [{
+        "ocrEngine": {
+          "ocrRegions": [{
+            "words": [{
+              "detectedText": "1",
+              "confidenceScore": 1.0
+            }]
+          }]
+        }
+      }]
     },
     {
       "imageId": "https://www.example.com/image3.jpg",
-      "status": {
-        "code": 8,
-        "message": "Resource exhaused"
-      }
+      "engineResults": [{
+        "status": {
+          "code": 8,
+          "message": "Resource exhaused"
+        }
+      }]
     }
   ]
 })";
@@ -729,23 +732,19 @@
       "ocr",
       ReformatJson(base::StringPrintf(kTemplateRequest, kImage1Url, "AQID")),
       R"({
-           "results": [
-             {
-               "imageId": "https://www.example.com/image1.jpg",
-               "annotations": {
-                 "ocrRegions": [
-                   {
-                     "words": [
-                        {
-                          "detectedText": "1",
-                          "confidenceScore": 1.0
-                        }
-                     ]
-                   }
-                 ]
+           "results": [{
+             "imageId": "https://www.example.com/image1.jpg",
+             "engineResults": [{
+               "ocrEngine": {
+                 "ocrRegions": [{
+                   "words": [{
+                     "detectedText": "1",
+                     "confidenceScore": 1.0
+                   }]
+                 }]
                }
-             }
-           ]
+             }]
+           }]
          })",
       net::HTTP_OK);
   EXPECT_THAT(test_url_factory.requests(), IsEmpty());
@@ -757,23 +756,19 @@
       "ocr",
       ReformatJson(base::StringPrintf(kTemplateRequest, kImage2Url, "BAUG")),
       R"({
-           "results": [
-             {
-               "imageId": "https://www.example.com/image2.jpg",
-               "annotations": {
-                 "ocrRegions": [
-                   {
-                     "words": [
-                        {
-                          "detectedText": "2",
-                          "confidenceScore": 1.0
-                        }
-                     ]
-                   }
-                 ]
+           "results": [{
+             "imageId": "https://www.example.com/image2.jpg",
+             "engineResults": [{
+               "ocrEngine": {
+                 "ocrRegions": [{
+                   "words": [{
+                     "detectedText": "2",
+                     "confidenceScore": 1.0
+                   }]
+                 }]
                }
-             }
-           ]
+             }]
+           }]
          })",
       net::HTTP_OK);
 
diff --git a/services/ws/gpu_host/gpu_host.cc b/services/ws/gpu_host/gpu_host.cc
index 1c45147c..078f6aa 100644
--- a/services/ws/gpu_host/gpu_host.cc
+++ b/services/ws/gpu_host/gpu_host.cc
@@ -175,9 +175,8 @@
 }
 
 void GpuHost::Shutdown() {
-  gpu_host_impl_.reset();
-
   gpu_clients_.clear();
+  gpu_host_impl_.reset();
 }
 
 void GpuHost::Add(mojom::GpuRequest request) {
diff --git a/services/ws/gpu_host/gpu_host_unittest.cc b/services/ws/gpu_host/gpu_host_unittest.cc
index 8c56f883..07546d6 100644
--- a/services/ws/gpu_host/gpu_host_unittest.cc
+++ b/services/ws/gpu_host/gpu_host_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "services/ws/gpu_host/gpu_host.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/macros.h"
@@ -48,7 +50,18 @@
       scoped_refptr<base::SingleThreadTaskRunner> io_runner);
   ~TestGpuService() override {}
 
+  // viz::GpuServiceImpl:
+  void EstablishGpuChannel(int32_t client_id,
+                           uint64_t client_tracing_id,
+                           bool is_gpu_host,
+                           bool cache_shaders_on_disk,
+                           EstablishGpuChannelCallback callback) override;
+
+  int channel_requests() const { return channel_requests_; }
+
  private:
+  int channel_requests_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(TestGpuService);
 };
 
@@ -64,6 +77,17 @@
                      nullptr /* vulkan_implementation */,
                      /*exit_callback=*/base::DoNothing()) {}
 
+void TestGpuService::EstablishGpuChannel(int32_t client_id,
+                                         uint64_t client_tracing_id,
+                                         bool is_gpu_host,
+                                         bool cache_shaders_on_disk,
+                                         EstablishGpuChannelCallback callback) {
+  channel_requests_++;
+  viz::GpuServiceImpl::EstablishGpuChannel(client_id, client_tracing_id,
+                                           is_gpu_host, cache_shaders_on_disk,
+                                           std::move(callback));
+}
+
 }  // namespace
 
 class GpuHostTest : public testing::Test {
@@ -79,11 +103,15 @@
 
   base::WeakPtr<viz::GpuClient> AddGpuClient();
   void DestroyHost();
+  void ShutdownHost();
 
   // testing::Test
   void SetUp() override;
   void TearDown() override;
 
+  // Flushes |io_thread_| tasks and returns the number of channel requests.
+  int GetChannelRequests();
+
  private:
   base::MessageLoop message_loop_;
 
@@ -92,7 +120,6 @@
   discardable_memory::DiscardableSharedMemoryManager
       discardable_memory_manager_;
   std::unique_ptr<TestGpuService> gpu_service_;
-  viz::mojom::GpuServicePtr gpu_service_ptr_;
   std::unique_ptr<GpuHost> gpu_host_;
 
   DISALLOW_COPY_AND_ASSIGN(GpuHostTest);
@@ -107,12 +134,17 @@
   gpu_host_.reset();
 }
 
+void GpuHostTest::ShutdownHost() {
+  gpu_host_->Shutdown();
+}
+
 void GpuHostTest::SetUp() {
   testing::Test::SetUp();
   gpu_host_ = std::make_unique<GpuHost>(&gpu_host_delegate_, nullptr,
                                         &discardable_memory_manager_);
-  gpu_service_->Bind(mojo::MakeRequest(&gpu_service_ptr_));
-  GpuHostTestApi(gpu_host_.get()).SetGpuService(std::move(gpu_service_ptr_));
+  viz::mojom::GpuServicePtr gpu_service_ptr;
+  gpu_service_->Bind(mojo::MakeRequest(&gpu_service_ptr));
+  GpuHostTestApi(gpu_host_.get()).SetGpuService(std::move(gpu_service_ptr));
 }
 
 void GpuHostTest::TearDown() {
@@ -121,6 +153,11 @@
   testing::Test::TearDown();
 }
 
+int GpuHostTest::GetChannelRequests() {
+  io_thread_.FlushForTesting();
+  return gpu_service_->channel_requests();
+}
+
 // Tests to verify, that if a GpuHost is deleted before viz::GpuClient receives
 // a callback, that viz::GpuClient is torn down and does not attempt to use
 // GpuInfo after deletion. This should not crash on asan-builds.
@@ -145,6 +182,24 @@
   EXPECT_TRUE(callback_called);
 }
 
+// Verifies that shutting down GpuHost while a channel request is in flight does
+// not retry that request.
+TEST_F(GpuHostTest, GpuHostShutdownWhileChannelRequestInFlight) {
+  base::WeakPtr<viz::GpuClient> client_ref = AddGpuClient();
+  mojom::Gpu* gpu = client_ref.get();
+
+  // Initially, there should be no channel requests.
+  EXPECT_EQ(0, GetChannelRequests());
+
+  // Send a channel request and verfiy it is received by gpu service.
+  gpu->EstablishGpuChannel(base::DoNothing());
+  EXPECT_EQ(1, GetChannelRequests());
+
+  // Shutting down host should not retry the pending channel request.
+  ShutdownHost();
+  EXPECT_EQ(1, GetChannelRequests());
+}
+
 }  // namespace test
 }  // namespace gpu_host
 }  // namespace ws
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 48e3268a..63b40c7 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -1047,6 +1047,53 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "NRD91N",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "expiration": 10800,
+          "hard_timeout": 960,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "boringssl_crypto_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -3498,52 +3545,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "NRD91N",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 960,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "webview_instrumentation_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index a92c1014..adcb4fce 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -927,6 +927,51 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "boringssl_crypto_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -3177,50 +3222,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_type": "hammerhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "wtf_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -3264,7 +3265,7 @@
           "--disable-breakpad",
           "--additional-driver-flag=--use-gpu-in-tests"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -3519,6 +3520,52 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84Z",
+              "device_type": "flo",
+              "os": "Android"
+            }
+          ],
+          "expiration": 10800,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "boringssl_crypto_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -5774,51 +5821,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84Z",
-              "device_type": "flo",
-              "os": "Android"
-            }
-          ],
-          "expiration": 10800,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "wtf_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -6137,6 +6139,53 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "LMY48I",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "expiration": 10800,
+          "hard_timeout": 960,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "boringssl_crypto_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -8588,52 +8637,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "LMY48I",
-              "device_type": "hammerhead",
-              "os": "Android"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 960,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "webview_instrumentation_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -8999,6 +9002,53 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "LMY49B",
+              "device_type": "flo",
+              "os": "Android"
+            }
+          ],
+          "expiration": 10800,
+          "hard_timeout": 120,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "boringssl_crypto_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -11305,52 +11355,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "LMY49B",
-              "device_type": "flo",
-              "os": "Android"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 120,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "webview_instrumentation_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -11704,6 +11708,52 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "hard_timeout": 1200,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "boringssl_crypto_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -14049,51 +14099,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "hard_timeout": 1200,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "webview_instrumentation_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -14449,6 +14454,52 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MRA58Z",
+              "device_type": "flo",
+              "os": "Android"
+            }
+          ],
+          "expiration": 10800,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "boringssl_crypto_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -16704,51 +16755,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MRA58Z",
-              "device_type": "flo",
-              "os": "Android"
-            }
-          ],
-          "expiration": 10800,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "webview_instrumentation_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -18642,6 +18648,51 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "boringssl_crypto_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -20892,50 +20943,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_type": "hammerhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "wtf_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -21319,6 +21326,51 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "boringssl_crypto_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -23570,50 +23622,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "webview_instrumentation_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index d74d8ce..81c2c4d 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -481,6 +481,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1079,12 +1086,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -1177,6 +1178,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1780,12 +1788,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index 7faed7c..9d2e2bd 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -58,6 +58,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -604,12 +611,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -722,6 +723,18 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Windows-10-15063"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -1774,17 +1787,6 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
         "test": "wm_unittests"
       },
       {
@@ -1913,6 +1915,18 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Windows-10-15063"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -2965,17 +2979,6 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
         "test": "wm_unittests"
       },
       {
@@ -3104,6 +3107,18 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Windows-10-15063"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -4144,17 +4159,6 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
         "test": "wm_unittests"
       },
       {
@@ -4454,6 +4458,51 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "boringssl_crypto_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -6658,50 +6707,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_type": "hammerhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "wtf_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -7106,6 +7111,51 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "boringssl_crypto_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -9310,50 +9360,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "webview_instrumentation_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -9495,6 +9501,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -10034,12 +10047,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -10151,6 +10158,16 @@
         "args": [
           "--test-launcher-print-test-stdio=always"
         ],
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-print-test-stdio=always"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -10901,15 +10918,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -10983,6 +10991,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -11516,12 +11531,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -11589,6 +11598,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -12128,12 +12144,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -12210,6 +12220,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -12711,12 +12728,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -12784,6 +12795,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -13323,12 +13341,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -13399,6 +13411,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -13938,12 +13957,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -14005,6 +14018,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -14501,17 +14521,18 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wtf_unittests"
       }
     ],
     "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
         "args": [
           "--test-type=integration"
         ],
@@ -14609,7 +14630,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -14622,13 +14643,6 @@
           "can_use_on_swarming_builders": true,
           "shards": 12
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        }
       }
     ]
   },
@@ -14710,6 +14724,16 @@
         "args": [
           "--test-launcher-print-test-stdio=always"
         ],
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-print-test-stdio=always"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -15336,15 +15360,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wtf_unittests"
       }
     ]
@@ -15406,6 +15421,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -16009,12 +16031,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -16088,6 +16104,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -16691,12 +16714,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -16770,6 +16787,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -17373,12 +17397,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -17452,6 +17470,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -18055,12 +18080,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -18134,6 +18153,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -18737,12 +18763,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -18816,6 +18836,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -19419,12 +19446,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -19498,6 +19519,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -20101,12 +20129,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -20180,6 +20202,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -20783,12 +20812,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -20862,6 +20885,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -21465,12 +21495,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -21544,6 +21568,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -22147,12 +22178,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -22226,6 +22251,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -22765,12 +22797,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -22883,6 +22909,18 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -23901,17 +23939,6 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10"
-            }
-          ]
-        },
         "test": "wm_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 6b363ca..d03a4fc2 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -379,6 +379,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -875,17 +882,18 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wtf_unittests"
       }
     ],
     "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
         "args": [
           "--test-type=integration"
         ],
@@ -978,13 +986,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        }
       }
     ]
   },
@@ -2123,7 +2124,7 @@
           "--additional-driver-flag=--enable-features=NetworkService",
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -2294,7 +2295,7 @@
           "--additional-driver-flag=--enable-features=NetworkService",
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -2428,7 +2429,7 @@
           "--num-retries=3",
           "--additional-driver-flag=--enable-blink-features=CompositeAfterPaint"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -2467,7 +2468,7 @@
           ],
           "hard_timeout": 900
         },
-        "test": "webkit_unit_tests"
+        "test": "blink_unittests"
       }
     ],
     "isolated_scripts": [
@@ -2476,7 +2477,7 @@
           "--num-retries=3",
           "--additional-driver-flag=--enable-blink-features=LayoutNG"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -2505,7 +2506,7 @@
           "--num-retries=3",
           "--additional-driver-flag=--root-layer-scrolls"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -2584,6 +2585,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": false
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": false
         },
@@ -3171,12 +3179,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "wm_unittests"
       },
       {
@@ -4715,7 +4717,7 @@
           "--num-retries=3",
           "--platform=fuchsia"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -4780,6 +4782,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4996,12 +5005,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wtf_unittests"
       }
     ],
@@ -5011,7 +5014,7 @@
           "--num-retries=3",
           "--debug"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -5060,6 +5063,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -5276,12 +5286,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wtf_unittests"
       }
     ],
@@ -5292,7 +5296,7 @@
           "--debug",
           "--additional-driver-flag=--enable-blink-features=HeapIncrementalMarkingStress"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -5359,6 +5363,16 @@
         "args": [
           "--enable-blink-features=HeapUnifiedGarbageCollection"
         ],
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--enable-blink-features=HeapUnifiedGarbageCollection"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -5673,15 +5687,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--enable-blink-features=HeapUnifiedGarbageCollection"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wtf_unittests"
       }
     ],
@@ -5692,7 +5697,7 @@
           "--debug",
           "--additional-driver-flag=--enable-blink-features=HeapUnifiedGarbageCollection"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -5741,6 +5746,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -5957,12 +5969,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wtf_unittests"
       }
     ],
@@ -5971,7 +5977,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -5993,7 +5999,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -6082,6 +6088,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -6621,12 +6634,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -6638,6 +6645,13 @@
     ],
     "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
         "args": [
           "--test-type=integration"
         ],
@@ -6690,7 +6704,7 @@
           "src/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials",
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -6772,7 +6786,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -6785,13 +6799,6 @@
           "can_use_on_swarming_builders": true,
           "shards": 12
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        }
       }
     ]
   },
@@ -6862,6 +6869,14 @@
       },
       {
         "isolate_coverage_data": true,
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -7469,13 +7484,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -10110,7 +10118,7 @@
           "--num-retries=3"
         ],
         "isolate_coverage_data": true,
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "name": "webkit_layout_tests",
         "results_handler": "layout tests",
         "swarming": {
@@ -10281,6 +10289,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -10820,12 +10835,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -10837,6 +10846,13 @@
     ],
     "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
         "args": [
           "--test-type=integration"
         ],
@@ -10889,7 +10905,7 @@
           "src/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials",
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -10971,7 +10987,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -10984,13 +11000,6 @@
           "can_use_on_swarming_builders": true,
           "shards": 12
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        }
       }
     ],
     "scripts": [
@@ -11012,7 +11021,7 @@
       },
       {
         "name": "webkit_lint",
-        "script": "webkit_lint.py"
+        "script": "blink_lint_expectations.py"
       }
     ]
   },
@@ -11113,7 +11122,7 @@
           "--additional-driver-flag=--enable-features=NetworkService",
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -11242,6 +11251,20 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.14"
+            }
+          ],
+          "expiration": 21600
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -12344,9 +12367,13 @@
           ],
           "expiration": 21600
         },
-        "test": "webkit_unit_tests"
-      },
+        "test": "wtf_unittests"
+      }
+    ],
+    "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -12356,11 +12383,8 @@
             }
           ],
           "expiration": 21600
-        },
-        "test": "wtf_unittests"
-      }
-    ],
-    "isolated_scripts": [
+        }
+      },
       {
         "args": [
           "--test-type=integration"
@@ -12522,7 +12546,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -12542,20 +12566,6 @@
           "expiration": 21600,
           "shards": 12
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.14"
-            }
-          ],
-          "expiration": 21600
-        }
       }
     ]
   },
@@ -12565,7 +12575,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -12593,7 +12603,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -12621,7 +12631,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -12650,7 +12660,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -12680,7 +12690,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -12718,7 +12728,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -12746,7 +12756,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
diff --git a/testing/buildbot/chromium.gpu.json b/testing/buildbot/chromium.gpu.json
index bd3151e..522546c5 100644
--- a/testing/buildbot/chromium.gpu.json
+++ b/testing/buildbot/chromium.gpu.json
@@ -1946,7 +1946,7 @@
       },
       {
         "name": "webkit_lint",
-        "script": "webkit_lint.py",
+        "script": "blink_lint_expectations.py",
         "swarming": {}
       }
     ]
@@ -2655,7 +2655,7 @@
       },
       {
         "name": "webkit_lint",
-        "script": "webkit_lint.py",
+        "script": "blink_lint_expectations.py",
         "swarming": {}
       }
     ]
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index b51612b..9d4ac2c9 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -38,6 +38,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -305,12 +312,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wtf_unittests"
       }
     ]
@@ -352,6 +353,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -631,12 +639,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wtf_unittests"
       }
     ]
@@ -1105,6 +1107,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1644,12 +1653,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -1661,6 +1664,13 @@
     ],
     "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
         "args": [
           "--test-type=integration"
         ],
@@ -1713,7 +1723,7 @@
           "src/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials",
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -1795,7 +1805,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -1813,13 +1823,6 @@
           ],
           "shards": 6
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        }
       }
     ],
     "scripts": [
@@ -1841,7 +1844,7 @@
       },
       {
         "name": "webkit_lint",
-        "script": "webkit_lint.py"
+        "script": "blink_lint_expectations.py"
       }
     ]
   },
@@ -1902,6 +1905,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -2442,12 +2452,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -2459,6 +2463,13 @@
     ],
     "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
         "isolate_name": "content_shell_crash_test",
         "name": "content_shell_crash_test",
         "swarming": {
@@ -2550,7 +2561,7 @@
           "--num-retries=3",
           "--debug"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -2568,13 +2579,6 @@
           ],
           "shards": 20
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        }
       }
     ]
   },
@@ -2635,6 +2639,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -3169,12 +3180,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -3186,6 +3191,13 @@
     ],
     "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
         "isolate_name": "devtools_closure_compile",
         "name": "devtools_closure_compile",
         "swarming": {
@@ -3250,13 +3262,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        }
       }
     ]
   },
@@ -3405,6 +3410,18 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -4349,17 +4366,6 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
         "test": "wm_unittests"
       },
       {
@@ -4376,6 +4382,18 @@
     ],
     "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        }
+      },
+      {
         "args": [
           "--test-type=integration"
         ],
@@ -4458,7 +4476,7 @@
           "src/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials",
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -4565,7 +4583,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -4583,18 +4601,6 @@
           ],
           "shards": 12
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        }
       }
     ],
     "scripts": [
@@ -4616,7 +4622,7 @@
       },
       {
         "name": "webkit_lint",
-        "script": "webkit_lint.py"
+        "script": "blink_lint_expectations.py"
       }
     ]
   }
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index ff06c3d4..9f5fafe 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -110,6 +110,19 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.10"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -967,9 +980,13 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
+        "test": "wtf_unittests"
+      }
+    ],
+    "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -978,11 +995,8 @@
               "os": "Mac-10.10"
             }
           ]
-        },
-        "test": "wtf_unittests"
-      }
-    ],
-    "isolated_scripts": [
+        }
+      },
       {
         "args": [
           "--test-type=integration"
@@ -1138,7 +1152,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -1157,19 +1171,6 @@
           ],
           "shards": 12
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "none",
-              "os": "Mac-10.10"
-            }
-          ]
-        }
       }
     ]
   },
@@ -1272,6 +1273,19 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.11"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -2129,9 +2143,13 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
+        "test": "wtf_unittests"
+      }
+    ],
+    "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -2140,11 +2158,8 @@
               "os": "Mac-10.11"
             }
           ]
-        },
-        "test": "wtf_unittests"
-      }
-    ],
-    "isolated_scripts": [
+        }
+      },
       {
         "args": [
           "--test-type=integration"
@@ -2300,7 +2315,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -2319,19 +2334,6 @@
           ],
           "shards": 12
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "none",
-              "os": "Mac-10.11"
-            }
-          ]
-        }
       }
     ]
   },
@@ -2434,6 +2436,19 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -3374,9 +3389,13 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
+        "test": "wtf_unittests"
+      }
+    ],
+    "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -3385,11 +3404,8 @@
               "os": "Mac-10.12.6"
             }
           ]
-        },
-        "test": "wtf_unittests"
-      }
-    ],
-    "isolated_scripts": [
+        }
+      },
       {
         "args": [
           "--test-type=integration"
@@ -3545,7 +3561,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -3564,19 +3580,6 @@
           ],
           "shards": 12
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.12.6"
-            }
-          ]
-        }
       }
     ]
   },
@@ -3679,6 +3682,19 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.13"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -4536,9 +4552,13 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
+        "test": "wtf_unittests"
+      }
+    ],
+    "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -4547,11 +4567,8 @@
               "os": "Mac-10.13"
             }
           ]
-        },
-        "test": "wtf_unittests"
-      }
-    ],
-    "isolated_scripts": [
+        }
+      },
       {
         "args": [
           "--test-type=integration"
@@ -4688,7 +4705,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -4707,19 +4724,6 @@
           ],
           "shards": 12
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "none",
-              "os": "Mac-10.13"
-            }
-          ]
-        }
       }
     ],
     "scripts": [
@@ -4730,7 +4734,7 @@
       },
       {
         "name": "webkit_lint",
-        "script": "webkit_lint.py",
+        "script": "blink_lint_expectations.py",
         "swarming": {}
       }
     ]
@@ -4834,6 +4838,19 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.13"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5691,9 +5708,13 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
+        "test": "wtf_unittests"
+      }
+    ],
+    "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5702,11 +5723,8 @@
               "os": "Mac-10.13"
             }
           ]
-        },
-        "test": "wtf_unittests"
-      }
-    ],
-    "isolated_scripts": [
+        }
+      },
       {
         "isolate_name": "content_shell_crash_test",
         "name": "content_shell_crash_test",
@@ -5819,7 +5837,7 @@
           "--num-retries=3",
           "--debug"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -5838,19 +5856,6 @@
           ],
           "shards": 12
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "none",
-              "os": "Mac-10.13"
-            }
-          ]
-        }
       }
     ]
   },
@@ -5860,7 +5865,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 5fd0bca..e92d0b6 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -271,6 +271,51 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "boringssl_crypto_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -2521,50 +2566,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "webview_instrumentation_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -2732,6 +2733,17 @@
         "args": [
           "--test-launcher-print-test-stdio=always"
         ],
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-print-test-stdio=always"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -3440,16 +3452,6 @@
           "--test-launcher-print-test-stdio=always"
         ],
         "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 5
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
           "can_use_on_swarming_builders": true
         },
         "test": "wm_unittests"
@@ -3564,6 +3566,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4128,12 +4137,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -4260,6 +4263,16 @@
         "args": [
           "--test-launcher-print-test-stdio=always"
         ],
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-print-test-stdio=always"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -5088,15 +5101,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -5298,6 +5302,22 @@
         "args": [
           "--test-launcher-print-test-stdio=always"
         ],
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Ubuntu-14.04"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-print-test-stdio=always"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -6633,21 +6653,6 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
-          ]
-        },
         "test": "wm_unittests"
       },
       {
@@ -6808,6 +6813,22 @@
         "args": [
           "--test-launcher-print-test-stdio=always"
         ],
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Ubuntu-14.04"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-print-test-stdio=always"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -8033,21 +8054,6 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
-          ]
-        },
         "test": "wm_unittests"
       },
       {
@@ -8154,6 +8160,16 @@
         "args": [
           "--test-launcher-print-test-stdio=always"
         ],
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-print-test-stdio=always"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -8844,15 +8860,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -8944,6 +8951,16 @@
         "args": [
           "--test-launcher-print-test-stdio=always"
         ],
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-print-test-stdio=always"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -9570,15 +9587,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wtf_unittests"
       }
     ]
@@ -9594,7 +9602,7 @@
           "48000",
           "--enable-sanitizer"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -9626,7 +9634,7 @@
           "48000",
           "--enable-leak-detection"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -9658,7 +9666,7 @@
           "66000",
           "--enable-sanitizer"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -9784,6 +9792,18 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Windows-10-15063"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -10836,17 +10856,6 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
         "test": "wm_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.webkit.json b/testing/buildbot/chromium.webkit.json
index 39d485091..bebe3fe 100644
--- a/testing/buildbot/chromium.webkit.json
+++ b/testing/buildbot/chromium.webkit.json
@@ -12,7 +12,7 @@
           "48000",
           "--enable-sanitizer"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -44,7 +44,7 @@
           "48000",
           "--enable-leak-detection"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -76,7 +76,7 @@
           "66000",
           "--enable-sanitizer"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -106,7 +106,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -136,7 +136,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index 9d53656..4997893 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -7,7 +7,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -85,6 +85,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -688,12 +695,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -711,6 +712,13 @@
     ],
     "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
         "args": [
           "--test-type=integration"
         ],
@@ -815,13 +823,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        }
       }
     ]
   },
@@ -947,6 +948,19 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-10-15063"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -2085,18 +2099,6 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
         "test": "wm_unittests"
       },
       {
@@ -2252,6 +2254,19 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-10-15063"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -3306,18 +3321,6 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
         "test": "wm_unittests"
       },
       {
@@ -3347,6 +3350,19 @@
     ],
     "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-10-15063"
+            }
+          ]
+        }
+      },
+      {
         "experiment_percentage": 100,
         "isolate_name": "content_shell_crash_test",
         "name": "content_shell_crash_test",
@@ -3455,19 +3471,6 @@
             }
           ]
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        }
       }
     ]
   },
@@ -3594,6 +3597,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4144,12 +4154,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -4167,6 +4171,13 @@
     ],
     "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
         "args": [
           "--test-type=integration"
         ],
@@ -4276,7 +4287,7 @@
         "args": [
           "--num-retries=3"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -4289,19 +4300,12 @@
           "can_use_on_swarming_builders": true,
           "shards": 12
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        }
       }
     ],
     "scripts": [
       {
         "name": "webkit_lint",
-        "script": "webkit_lint.py"
+        "script": "blink_lint_expectations.py"
       }
     ]
   },
@@ -4362,6 +4366,13 @@
         "test": "blink_platform_unittests"
       },
       {
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4966,12 +4977,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -4989,6 +4994,13 @@
     ],
     "isolated_scripts": [
       {
+        "isolate_name": "blink_python_tests",
+        "name": "blink_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
         "isolate_name": "content_shell_crash_test",
         "name": "content_shell_crash_test",
         "swarming": {
@@ -5081,7 +5093,7 @@
           "60000"
         ],
         "experiment_percentage": 100,
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
@@ -5099,13 +5111,6 @@
           ],
           "shards": 12
         }
-      },
-      {
-        "isolate_name": "webkit_python_tests",
-        "name": "webkit_python_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        }
       }
     ]
   },
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 7efc4828..fad0255 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -305,6 +305,14 @@
     "label": "//third_party/blink/renderer/platform:blink_png_decoder_fuzzer",
     "type": "fuzzer",
   },
+  "blink_python_tests": {
+    "args": [
+      "../../third_party/blink/tools/run_blinkpy_tests.py",
+    ],
+    "label": "//:blink_python_tests",
+    "script": "//testing/scripts/run_isolated_script_test.py",
+    "type": "script",
+  },
   "blink_tests": {
     "label": "//:blink_tests",
     "type": "additional_compile_target",
@@ -317,6 +325,30 @@
     "label": "//third_party/blink/renderer/platform:blink_text_codec_WINDOWS_1252_fuzzer",
     "type": "fuzzer",
   },
+  "blink_unittests": {
+    "label": "//third_party/blink/renderer/controller:blink_unittests",
+    "type": "console_test_launcher",
+  },
+  "blink_web_tests_exparchive": {
+    "args": [
+      "../../third_party/blink/tools/run_web_tests.py",
+      "--seed",
+      "4",
+      "--no-show-results",
+      "--zero-tests-executed-ok",
+      "--clobber-old-results",
+      "--exit-after-n-failures",
+      "5000",
+      "--exit-after-n-crashes-or-timeouts",
+      "100",
+      "--debug-rwt-logging",
+      "--results-directory",
+      "${ISOLATED_OUTDIR}/layout-test-results",
+    ],
+    "label": "//:blink_web_tests_exparchive",
+    "script": "//testing/scripts/run_isolated_script_test.py",
+    "type": "script",
+  },
   "boringssl_arm_cpuinfo_fuzzer": {
     "label": "//third_party/boringssl:boringssl_arm_cpuinfo_fuzzer",
     "type": "fuzzer",
@@ -2724,42 +2756,10 @@
       "--results-directory",
       "${ISOLATED_OUTDIR}/layout-test-results",
     ],
-    "label": "//:webkit_layout_tests",
+    "label": "//:blink_web_tests",
     "script": "//testing/scripts/run_isolated_script_test.py",
     "type": "script",
   },
-  "webkit_layout_tests_exparchive": {
-    "args": [
-      "../../third_party/blink/tools/run_web_tests.py",
-      "--seed",
-      "4",
-      "--no-show-results",
-      "--zero-tests-executed-ok",
-      "--clobber-old-results",
-      "--exit-after-n-failures",
-      "5000",
-      "--exit-after-n-crashes-or-timeouts",
-      "100",
-      "--debug-rwt-logging",
-      "--results-directory",
-      "${ISOLATED_OUTDIR}/layout-test-results",
-    ],
-    "label": "//:webkit_layout_tests_exparchive",
-    "script": "//testing/scripts/run_isolated_script_test.py",
-    "type": "script",
-  },
-  "webkit_python_tests": {
-    "args": [
-      "../../third_party/blink/tools/run_blinkpy_tests.py",
-    ],
-    "label": "//:webkit_python_tests",
-    "script": "//testing/scripts/run_isolated_script_test.py",
-    "type": "script",
-  },
-  "webkit_unit_tests": {
-    "label": "//third_party/blink/renderer/controller:webkit_unit_tests",
-    "type": "console_test_launcher",
-  },
   "webusb_descriptors_fuzzer": {
     "label": "//device/usb:webusb_descriptors_fuzzer",
     "type": "fuzzer",
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index d769530..b4b10d4 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -547,7 +547,9 @@
 
     'chromium_android_webkit_gtests': {
       'blink_heap_unittests': {},
-      'webkit_unit_tests': {},
+      'webkit_unit_tests': {
+        'test': 'blink_unittests',
+      },
     },
 
     'chromium_browser_tests': {
@@ -2279,7 +2281,7 @@
           '--additional-env-var=LLVM_PROFILE_FILE=${ISOLATED_OUTDIR}/profraw/default-%8m.profraw',
           '--num-retries=3',
         ],
-        'isolate_name': 'webkit_layout_tests_exparchive',
+        'isolate_name': 'blink_web_tests_exparchive',
         'results_handler': 'layout tests',
         'swarming': {
           'shards': 12,
@@ -2357,6 +2359,7 @@
       'ui_touch_selection_unittests': {},
       'url_unittests': {},
       'webkit_unit_tests': {
+        'test': 'blink_unittests',
         'android_swarming': {
           'shards': 2,
         },
@@ -2423,7 +2426,7 @@
         'script': 'headless_python_unittests.py',
       },
       'webkit_lint': {
-        'script': 'webkit_lint.py',
+        'script': 'blink_lint_expectations.py',
       },
     },
 
@@ -2432,13 +2435,13 @@
         'script': 'check_static_initializers.py',
       },
       'webkit_lint': {
-        'script': 'webkit_lint.py',
+        'script': 'blink_lint_expectations.py',
       },
     },
 
     'chromium_scripts': {
       'webkit_lint': {
-        'script': 'webkit_lint.py',
+        'script': 'blink_lint_expectations.py',
       },
     },
 
@@ -2570,7 +2573,7 @@
         'args': [
           '--num-retries=3',
         ],
-        'isolate_name': 'webkit_layout_tests_exparchive',
+        'isolate_name': 'blink_web_tests_exparchive',
         'merge': {
           'args': [
             '--verbose',
@@ -2725,7 +2728,7 @@
         'args': [
           '--num-retries=3',
         ],
-        'isolate_name': 'webkit_layout_tests_exparchive',
+        'isolate_name': 'blink_web_tests_exparchive',
         'merge': {
           'args': [
             '--verbose',
@@ -2737,7 +2740,7 @@
           'shards': 12,
         }
       },
-      'webkit_python_tests': {},
+      'blink_python_tests': {},
     },
 
     'fuchsia_gtests': {
@@ -3524,7 +3527,7 @@
         'args': [
           '--enable-blink-features=LayoutNG'
         ],
-        'test': 'webkit_unit_tests',
+        'test': 'blink_unittests',
       },
     },
 
@@ -3681,7 +3684,7 @@
           # retry 3 times, so we explicitly specify it.
           '--num-retries=3',
         ],
-        'isolate_name': 'webkit_layout_tests_exparchive',
+        'isolate_name': 'blink_web_tests_exparchive',
         'merge': {
           'args': [
             '--verbose',
@@ -3779,7 +3782,7 @@
           # retry 3 times, so we explicitly specify it.
           '--num-retries=3',
         ],
-        'isolate_name': 'webkit_layout_tests_exparchive',
+        'isolate_name': 'blink_web_tests_exparchive',
         'merge': {
           'args': [
             '--verbose',
diff --git a/testing/buildbot/tryserver.chromium.android.json b/testing/buildbot/tryserver.chromium.android.json
index 30ab9b5..5583788 100644
--- a/testing/buildbot/tryserver.chromium.android.json
+++ b/testing/buildbot/tryserver.chromium.android.json
@@ -60,6 +60,7 @@
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
+        "name": "webkit_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
@@ -87,7 +88,7 @@
             }
           ]
         },
-        "test": "webkit_unit_tests"
+        "test": "blink_unittests"
       }
     ],
     "isolated_scripts": [
@@ -99,7 +100,7 @@
           "--disable-breakpad",
           "--additional-driver-flag=--use-gpu-in-tests"
         ],
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "merge": {
           "args": [
             "--verbose"
diff --git a/testing/buildbot/tryserver.chromium.linux.json b/testing/buildbot/tryserver.chromium.linux.json
index 36ce76c6..b107bcf8 100644
--- a/testing/buildbot/tryserver.chromium.linux.json
+++ b/testing/buildbot/tryserver.chromium.linux.json
@@ -68,6 +68,14 @@
       },
       {
         "isolate_coverage_data": true,
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -675,13 +683,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
@@ -700,7 +701,7 @@
           "--num-retries=3"
         ],
         "isolate_coverage_data": true,
-        "isolate_name": "webkit_layout_tests_exparchive",
+        "isolate_name": "blink_web_tests_exparchive",
         "name": "webkit_layout_tests",
         "results_handler": "layout tests",
         "swarming": {
diff --git a/testing/buildbot/tryserver.webrtc.json b/testing/buildbot/tryserver.webrtc.json
index b57d1b6..03a7ca7 100644
--- a/testing/buildbot/tryserver.webrtc.json
+++ b/testing/buildbot/tryserver.webrtc.json
@@ -3,49 +3,49 @@
   "AAAAA2 See generate_buildbot_json.py to make changes": {},
   "android_chromium_compile": {
     "additional_compile_targets": [
+      "blink_unittests",
       "browser_tests",
       "capture_unittests",
       "content_browsertests",
       "content_unittests",
       "jingle_unittests",
-      "remoting_unittests",
-      "webkit_unit_tests"
+      "remoting_unittests"
     ]
   },
   "linux_chromium_compile": {
     "additional_compile_targets": [
+      "blink_unittests",
       "browser_tests",
       "capture_unittests",
       "content_browsertests",
       "content_unittests",
       "jingle_unittests",
       "remoting_unittests",
-      "remoting/webapp:webapp",
-      "webkit_unit_tests"
+      "remoting/webapp:webapp"
     ]
   },
   "mac_chromium_compile": {
     "additional_compile_targets": [
+      "blink_unittests",
       "browser_tests",
       "capture_unittests",
       "content_browsertests",
       "content_unittests",
       "jingle_unittests",
       "remoting_unittests",
-      "remoting/webapp:webapp",
-      "webkit_unit_tests"
+      "remoting/webapp:webapp"
     ]
   },
   "win_chromium_compile": {
     "additional_compile_targets": [
+      "blink_unittests",
       "browser_tests",
       "capture_unittests",
       "content_browsertests",
       "content_unittests",
       "jingle_unittests",
       "remoting_unittests",
-      "remoting/webapp:webapp",
-      "webkit_unit_tests"
+      "remoting/webapp:webapp"
     ]
   }
 }
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 74a4bfdb..5da913c8 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3840,17 +3840,18 @@
     'machines': {
       'android_chromium_compile': {
         'additional_compile_targets': [
+          'blink_unittests',
           'browser_tests',
           'capture_unittests',
           'content_browsertests',
           'content_unittests',
           'jingle_unittests',
           'remoting_unittests',
-          'webkit_unit_tests',
         ],
       },
       'linux_chromium_compile': {
         'additional_compile_targets': [
+          'blink_unittests',
           'browser_tests',
           'capture_unittests',
           'content_browsertests',
@@ -3858,11 +3859,11 @@
           'jingle_unittests',
           'remoting_unittests',
           'remoting/webapp:webapp',
-          'webkit_unit_tests',
         ],
       },
       'mac_chromium_compile': {
         'additional_compile_targets': [
+          'blink_unittests',
           'browser_tests',
           'capture_unittests',
           'content_browsertests',
@@ -3870,11 +3871,11 @@
           'jingle_unittests',
           'remoting_unittests',
           'remoting/webapp:webapp',
-          'webkit_unit_tests',
         ],
       },
       'win_chromium_compile': {
         'additional_compile_targets': [
+          'blink_unittests',
           'browser_tests',
           'capture_unittests',
           'content_browsertests',
@@ -3882,7 +3883,6 @@
           'jingle_unittests',
           'remoting_unittests',
           'remoting/webapp:webapp',
-          'webkit_unit_tests',
         ],
       },
     },
diff --git a/testing/scripts/webkit_lint.py b/testing/scripts/blink_lint_expectations.py
similarity index 100%
rename from testing/scripts/webkit_lint.py
rename to testing/scripts/blink_lint_expectations.py
diff --git a/testing/scripts/webkit_python_tests.py b/testing/scripts/blink_python_tests.py
similarity index 100%
rename from testing/scripts/webkit_python_tests.py
rename to testing/scripts/blink_python_tests.py
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 8739f72a..67bb611c 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -192,10 +192,6 @@
 const base::Feature kAlwaysAccelerateCanvas{"AlwaysAccelerateCanvas",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables cache-aware WebFonts loading. See https://crbug.com/570205.
-const base::Feature kWebFontsCacheAwareTimeoutAdaption{
-    "WebFontsCacheAwareTimeoutAdaption", base::FEATURE_ENABLED_BY_DEFAULT};
-
 bool IsOffMainThreadSharedWorkerScriptFetchEnabled() {
   // Off-the-main-thread shared worker script fetch depends on PlzSharedWorker
   // (NetworkService).
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 6a1f25f7..6a5d53d6 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -43,7 +43,7 @@
     "//third_party/blink/common",
     "//third_party/blink/common:blink_common_unittests",
     "//third_party/blink/renderer/controller",
-    "//third_party/blink/renderer/controller:webkit_unit_tests",
+    "//third_party/blink/renderer/controller:blink_unittests",
     "//third_party/blink/renderer/core",
     "//third_party/blink/renderer/modules",
     "//third_party/blink/renderer/platform:blink_platform_unittests",
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index ea1e88b..d3c1132 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -64,9 +64,6 @@
 BLINK_COMMON_EXPORT extern const base::Feature kDecodeLossyWebPImagesToYUV;
 BLINK_COMMON_EXPORT extern const base::Feature kAlwaysAccelerateCanvas;
 
-BLINK_COMMON_EXPORT extern const base::Feature
-    kWebFontsCacheAwareTimeoutAdaption;
-
 // Returns true when off-the-main-thread shared worker script fetch is enabled.
 BLINK_COMMON_EXPORT bool IsOffMainThreadSharedWorkerScriptFetchEnabled();
 
diff --git a/third_party/blink/public/platform/web_video_frame_submitter.h b/third_party/blink/public/platform/web_video_frame_submitter.h
index 75b5051..b31ce8ab 100644
--- a/third_party/blink/public/platform/web_video_frame_submitter.h
+++ b/third_party/blink/public/platform/web_video_frame_submitter.h
@@ -18,19 +18,19 @@
 }
 
 namespace viz {
-class ContextProvider;
+class RasterContextProvider;
 }  // namespace viz
 
 namespace blink {
 
 // Sets the proper context_provider and compositing mode onto the Submitter.
 using WebSubmitterConfigurationCallback =
-    base::OnceCallback<void(bool, scoped_refptr<viz::ContextProvider>)>;
+    base::OnceCallback<void(bool, scoped_refptr<viz::RasterContextProvider>)>;
 
-// Callback to obtain the media ContextProvider and a bool indicating whether
-// we are in software compositing mode.
+// Callback to obtain the media RasterContextProvider and a bool indicating
+// whether we are in software compositing mode.
 using WebContextProviderCallback =
-    base::RepeatingCallback<void(scoped_refptr<viz::ContextProvider>,
+    base::RepeatingCallback<void(scoped_refptr<viz::RasterContextProvider>,
                                  WebSubmitterConfigurationCallback)>;
 
 // Exposes the VideoFrameSubmitter, which submits CompositorFrames containing
diff --git a/third_party/blink/renderer/controller/BUILD.gn b/third_party/blink/renderer/controller/BUILD.gn
index 56bfdc0..65c03e2a 100644
--- a/third_party/blink/renderer/controller/BUILD.gn
+++ b/third_party/blink/renderer/controller/BUILD.gn
@@ -69,7 +69,7 @@
   configs += blink_symbols_config
 }
 
-group("webkit_unit_tests_data") {
+group("blink_unittests_data") {
   data = [
     "../core/testing/data/",
     "../core/paint/test_data/",
@@ -78,13 +78,24 @@
   ]
 }
 
-test("webkit_unit_tests") {
+# Old name of blink_unittests.
+# TODO(tkent): Remove this after removing external dependency.
+group("webkit_unit_tests") {
+  visibility = []  # Allow re-assignment of list.
+  visibility = [ "*" ]
+  testonly = true
   deps = [
-    ":webkit_unit_tests_sources",
+    ":blink_unittests",
+  ]
+}
+
+test("blink_unittests") {
+  deps = [
+    ":blink_unittests_sources",
   ]
 
   data_deps = [
-    ":webkit_unit_tests_data",
+    ":blink_unittests_data",
     "//content/shell:pak",
   ]
 
@@ -128,7 +139,7 @@
   ]
 }
 
-jumbo_source_set("webkit_unit_tests_sources") {
+jumbo_source_set("blink_unittests_sources") {
   visibility = []  # Allow re-assignment of list.
   visibility = [ "*" ]
   testonly = true
diff --git a/third_party/blink/renderer/controller/memory_usage_monitor.h b/third_party/blink/renderer/controller/memory_usage_monitor.h
index 62b26a3..7b8e120 100644
--- a/third_party/blink/renderer/controller/memory_usage_monitor.h
+++ b/third_party/blink/renderer/controller/memory_usage_monitor.h
@@ -8,6 +8,7 @@
 #include "base/observer_list.h"
 #include "third_party/blink/renderer/controller/controller_export.h"
 #include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 
@@ -24,6 +25,8 @@
 // Periodically checks the memory usage and notifies its observers. Monitoring
 // automatically starts/stops depending on whether an observer exists.
 class CONTROLLER_EXPORT MemoryUsageMonitor {
+  USING_FAST_MALLOC(MemoryUsageMonitor);
+
  public:
   static MemoryUsageMonitor& Instance();
 
diff --git a/third_party/blink/renderer/core/css/css_font_face_src_value.cc b/third_party/blink/renderer/core/css/css_font_face_src_value.cc
index 3db4ccb..5499dfe 100644
--- a/third_party/blink/renderer/core/css/css_font_face_src_value.cc
+++ b/third_party/blink/renderer/core/css/css_font_face_src_value.cc
@@ -25,8 +25,6 @@
 
 #include "third_party/blink/renderer/core/css/css_font_face_src_value.h"
 
-#include "base/feature_list.h"
-#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/core/css/css_markup.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
@@ -90,10 +88,7 @@
     ResourceLoaderOptions options;
     options.initiator_info.name = fetch_initiator_type_names::kCSS;
     FetchParameters params(resource_request, options);
-    if (base::FeatureList::IsEnabled(
-            features::kWebFontsCacheAwareTimeoutAdaption)) {
-      params.SetCacheAwareLoadingEnabled(kIsCacheAwareLoadingEnabled);
-    }
+    params.SetCacheAwareLoadingEnabled(kIsCacheAwareLoadingEnabled);
     params.SetContentSecurityCheck(should_check_content_security_policy_);
     const SecurityOrigin* security_origin = context->GetSecurityOrigin();
 
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index e0b3642..3ff5281c 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -1823,4 +1823,42 @@
   EXPECT_FALSE(span_inner->GetComputedStyle());
 }
 
+TEST_F(StyleEngineTest, EnsureCustomComputedStyle) {
+  GetDocument().body()->SetInnerHTMLFromString("");
+  GetDocument().body()->SetInnerHTMLFromString(R"HTML(
+    <div id=div>
+      <progress id=progress>
+    </div>
+  )HTML");
+  UpdateAllLifecyclePhases();
+
+  // Note: <progress> is chosen because it creates ProgressShadowElement
+  // instances, which override CustomStyleForLayoutObject with
+  // display:none.
+  Element* div = GetDocument().getElementById("div");
+  Element* progress = GetDocument().getElementById("progress");
+  ASSERT_TRUE(div);
+  ASSERT_TRUE(progress);
+
+  // This causes ProgressShadowElements to get ComputedStyles with
+  // IsEnsuredInDisplayNone==true.
+  for (Node* node = progress; node;
+       node = FlatTreeTraversal::Next(*node, progress)) {
+    node->EnsureComputedStyle();
+  }
+
+  // This triggers layout tree building.
+  div->SetInlineStyleProperty(CSSPropertyDisplay, "inline");
+  UpdateAllLifecyclePhases();
+
+  // We must not create LayoutObjects for Nodes with
+  // IsEnsuredInDisplayNone==true
+  for (Node* node = progress; node;
+       node = FlatTreeTraversal::Next(*node, progress)) {
+    ASSERT_TRUE(!node->GetComputedStyle() ||
+                !node->ComputedStyleRef().IsEnsuredInDisplayNone() ||
+                !node->GetLayoutObject());
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.h b/third_party/blink/renderer/core/display_lock/display_lock_context.h
index 1fc666ce..03bc95d 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.h
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/display_lock/display_lock_budget.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/compiler.h"
 
 namespace blink {
@@ -51,6 +52,8 @@
 
   // See GetScopedPendingFrameRect() for description.
   class ScopedPendingFrameRect {
+    STACK_ALLOCATED();
+
    public:
     ScopedPendingFrameRect(ScopedPendingFrameRect&&);
     ~ScopedPendingFrameRect();
@@ -65,6 +68,8 @@
 
   // See GetScopedForcedUpdate() for description.
   class ScopedForcedUpdate {
+    DISALLOW_NEW();
+
    public:
     ScopedForcedUpdate(ScopedForcedUpdate&&);
     ~ScopedForcedUpdate();
@@ -159,8 +164,10 @@
   };
 
   class StateChangeHelper {
+    DISALLOW_NEW();
+
    public:
-    StateChangeHelper(DisplayLockContext*);
+    explicit StateChangeHelper(DisplayLockContext*);
 
     operator State() const { return state_; }
     StateChangeHelper& operator=(State);
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 5000d9c..407f07c 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2585,26 +2585,6 @@
   }
 }
 
-scoped_refptr<ComputedStyle>
-Document::StyleForElementIgnoringPendingStylesheets(Element* element) {
-  DCHECK_EQ(element->GetDocument(), this);
-  StyleEngine::IgnoringPendingStylesheet ignoring(GetStyleEngine());
-  if (!element->CanParticipateInFlatTree())
-    return EnsureStyleResolver().StyleForElement(element, nullptr);
-
-  ContainerNode* parent = LayoutTreeBuilderTraversal::Parent(*element);
-  const ComputedStyle* parent_style =
-      parent ? parent->EnsureComputedStyle() : nullptr;
-
-  ContainerNode* layout_parent =
-      parent ? LayoutTreeBuilderTraversal::LayoutParent(*element) : nullptr;
-  const ComputedStyle* layout_parent_style =
-      layout_parent ? layout_parent->EnsureComputedStyle() : parent_style;
-
-  return EnsureStyleResolver().StyleForElement(element, parent_style,
-                                               layout_parent_style);
-}
-
 scoped_refptr<ComputedStyle> Document::StyleForPage(int page_index) {
   UpdateDistributionForUnknownReasons();
   return EnsureStyleResolver().StyleForPage(page_index);
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 473529a..fa6f25f 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -525,8 +525,6 @@
   void UpdateStyleAndLayoutIgnorePendingStylesheetsConsideringInvisibleNodes(
       RunPostLayoutTasks = kRunPostLayoutTasksAsynchronously);
   void UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(Node*);
-  scoped_refptr<ComputedStyle> StyleForElementIgnoringPendingStylesheets(
-      Element*);
   scoped_refptr<ComputedStyle> StyleForPage(int page_index);
 
   // Ensures that location-based data will be valid for a given node.
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index bfdecf2..66272331 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2202,7 +2202,6 @@
 }
 
 scoped_refptr<ComputedStyle> Element::OriginalStyleForLayoutObject() {
-  DCHECK(GetDocument().InStyleRecalc());
   return GetDocument().EnsureStyleResolver().StyleForElement(this);
 }
 
@@ -3830,8 +3829,21 @@
   // values returned for the ":selection" pseudo-element will be correct.
   ComputedStyle* element_style = MutableComputedStyle();
   if (!element_style) {
+    StyleEngine::IgnoringPendingStylesheet ignoring(
+        GetDocument().GetStyleEngine());
+    if (CanParticipateInFlatTree()) {
+      ContainerNode* parent = LayoutTreeBuilderTraversal::Parent(*this);
+      if (parent)
+        parent->EnsureComputedStyle();
+
+      ContainerNode* layout_parent =
+          parent ? LayoutTreeBuilderTraversal::LayoutParent(*this) : nullptr;
+      if (layout_parent)
+        layout_parent->EnsureComputedStyle();
+    }
     scoped_refptr<ComputedStyle> new_style =
-        GetDocument().StyleForElementIgnoringPendingStylesheets(this);
+        HasCustomStyleCallbacks() ? CustomStyleForLayoutObject()
+                                  : OriginalStyleForLayoutObject();
     element_style = new_style.get();
     element_style->SetIsEnsuredInDisplayNone();
     SetComputedStyle(std::move(new_style));
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder.cc b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
index c6cc46b..28d64ae1 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder.cc
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
@@ -78,7 +78,6 @@
     : LayoutTreeBuilder(element, nullptr), style_(style) {
   DCHECK(element.CanParticipateInFlatTree());
   DCHECK(style_);
-  DCHECK(!style_->IsEnsuredInDisplayNone());
   // TODO(ecobos): Move the first-letter logic inside ParentLayoutObject too?
   // It's an extra (unnecessary) check for text nodes, though.
   if (element.IsFirstLetterPseudoElement()) {
diff --git a/third_party/blink/renderer/core/editing/inline_box_position.h b/third_party/blink/renderer/core/editing/inline_box_position.h
index 464ff16..61f253c2 100644
--- a/third_party/blink/renderer/core/editing/inline_box_position.h
+++ b/third_party/blink/renderer/core/editing/inline_box_position.h
@@ -82,7 +82,7 @@
     const PositionInFlatTreeWithAffinity&);
 
 // The print for |InlineBoxPosition| is available only for testing
-// in "webkit_unit_tests", and implemented in
+// in "blink_unittests", and implemented in
 // "core/editing/inline_box_position_test.cc".
 std::ostream& operator<<(std::ostream&, const InlineBoxPosition&);
 
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
index 1d14e5b9a..a77c182 100644
--- a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
+++ b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
@@ -50,18 +50,55 @@
 
  public:
   // https://w3c.github.io/DOM-Parsing/#dfn-add
+  //
+  // This function doesn't accept empty prefix and empty namespace URI.
+  //  - The default namespace is managed separately.
+  //  - Namespace URI never be empty if the prefix is not empty.
   void Add(const AtomicString& prefix, const AtomicString& namespace_uri) {
-    const AtomicString& non_null_prefix = prefix ? prefix : g_empty_atom;
-    prefix_ns_map_.Set(non_null_prefix, namespace_uri);
-    auto result = ns_prefixes_map_.insert(
-        namespace_uri ? namespace_uri : g_empty_atom, Vector<AtomicString>());
-    result.stored_value->value.push_back(non_null_prefix);
+    DCHECK(!prefix.IsEmpty())
+        << " prefix=" << prefix << " namespace_uri=" << namespace_uri;
+    DCHECK(!namespace_uri.IsEmpty())
+        << " prefix=" << prefix << " namespace_uri=" << namespace_uri;
+    prefix_ns_map_.Set(prefix, namespace_uri);
+    auto result =
+        ns_prefixes_map_.insert(namespace_uri, Vector<AtomicString>());
+    result.stored_value->value.push_back(prefix);
+  }
+
+  // https://w3c.github.io/DOM-Parsing/#dfn-recording-the-namespace-information
+  void RecordNamespaceInformation(const Element& element) {
+    local_default_namespace_ = AtomicString();
+    // 2. For each attribute attr in element's attributes, in the order they are
+    // specified in the element's attribute list:
+    for (const auto& attr : element.Attributes()) {
+      // We don't check xmlns namespace of attr here because xmlns attributes in
+      // HTML documents don't have namespace URI. Some web tests serialize
+      // HTML documents with XMLSerializer, and Firefox has the same behavior.
+      if (attr.Prefix().IsEmpty() && attr.LocalName() == g_xmlns_atom) {
+        // 3.1. If attribute prefix is null, then attr is a default namespace
+        // declaration. Set the default namespace attr value to attr's value
+        // and stop running these steps, returning to Main to visit the next
+        // attribute.
+        local_default_namespace_ = attr.Value();
+      } else if (attr.Prefix() == g_xmlns_atom) {
+        Add(attr.Prefix() ? attr.LocalName() : g_empty_atom, attr.Value());
+      }
+    }
   }
 
   AtomicString LookupNamespaceURI(const AtomicString& prefix) const {
     return prefix_ns_map_.at(prefix ? prefix : g_empty_atom);
   }
 
+  const AtomicString& ContextNamespace() const { return context_namespace_; }
+  void SetContextNamespace(const AtomicString& context_ns) {
+    context_namespace_ = context_ns;
+  }
+
+  const AtomicString& LocalDefaultNamespace() const {
+    return local_default_namespace_;
+  }
+
   const Vector<AtomicString> PrefixList(const AtomicString& ns) const {
     return ns_prefixes_map_.at(ns ? ns : g_empty_atom);
   }
@@ -75,9 +112,11 @@
   using NamespaceToPrefixesMap = HashMap<AtomicString, Vector<AtomicString>>;
   NamespaceToPrefixesMap ns_prefixes_map_;
 
-  // TODO(tkent): Add 'context namespace' field. It is required to fix
-  // crbug.com/929035.
   // https://w3c.github.io/DOM-Parsing/#dfn-context-namespace
+  AtomicString context_namespace_;
+
+  // https://w3c.github.io/DOM-Parsing/#dfn-local-default-namespace
+  AtomicString local_default_namespace_;
 };
 
 MarkupAccumulator::MarkupAccumulator(EAbsoluteURLs resolve_urls_method,
@@ -126,22 +165,34 @@
 }
 
 void MarkupAccumulator::AppendElement(const Element& element) {
-  // https://html.spec.whatwg.org/C/#html-fragment-serialisation-algorithm
-  RecordNamespaceInformation(element);
-  AppendStartTagOpen(element);
-
-  AttributeCollection attributes = element.Attributes();
   if (SerializeAsHTMLDocument(element)) {
+    // https://html.spec.whatwg.org/C/#html-fragment-serialisation-algorithm
+    AppendStartTagOpen(element);
+    AttributeCollection attributes = element.Attributes();
     // 3.2. Element: If current node's is value is not null, and the
     // element does not have an is attribute in its attribute list, ...
     const AtomicString& is_value = element.IsValue();
     if (!is_value.IsNull() && !attributes.Find(html_names::kIsAttr)) {
       AppendAttribute(element, Attribute(html_names::kIsAttr, is_value));
     }
-  }
-  for (const auto& attribute : attributes) {
-    if (!ShouldIgnoreAttribute(element, attribute))
-      AppendAttribute(element, attribute);
+    for (const auto& attribute : attributes) {
+      if (!ShouldIgnoreAttribute(element, attribute))
+        AppendAttribute(element, attribute);
+    }
+  } else {
+    // https://w3c.github.io/DOM-Parsing/#xml-serializing-an-element-node
+    namespace_stack_.back().RecordNamespaceInformation(element);
+    const bool ignore_namespace_definition_attribute =
+        AppendStartTagOpen(element);
+
+    for (const auto& attribute : element.Attributes()) {
+      if (ignore_namespace_definition_attribute &&
+          attribute.NamespaceURI() == xmlns_names::kNamespaceURI &&
+          attribute.Prefix().IsEmpty())
+        continue;
+      if (!ShouldIgnoreAttribute(element, attribute))
+        AppendAttribute(element, attribute);
+    }
   }
 
   // Give an opportunity to subclasses to add their own attributes.
@@ -150,11 +201,80 @@
   AppendStartTagClose(element);
 }
 
-void MarkupAccumulator::AppendStartTagOpen(const Element& element) {
-  formatter_.AppendStartTagOpen(markup_, element);
-  if (!SerializeAsHTMLDocument(element) && ShouldAddNamespaceElement(element)) {
-    AppendNamespace(element.prefix(), element.namespaceURI());
+bool MarkupAccumulator::AppendStartTagOpen(const Element& element) {
+  if (SerializeAsHTMLDocument(element)) {
+    formatter_.AppendStartTagOpen(markup_, element);
+    return false;
   }
+
+  // https://w3c.github.io/DOM-Parsing/#xml-serializing-an-element-node
+
+  NamespaceContext& namespace_context = namespace_stack_.back();
+
+  // 5. Let ignore namespace definition attribute be a boolean flag with value
+  // false.
+  bool ignore_namespace_definition_attribute = false;
+  // 8. Let local default namespace be the result of recording the namespace
+  // information for node given map and local prefixes map.
+  AtomicString local_default_namespace =
+      namespace_stack_.back().LocalDefaultNamespace();
+  // 9. Let inherited ns be a copy of namespace.
+  AtomicString inherited_ns = namespace_context.ContextNamespace();
+  // 10. Let ns be the value of node's namespaceURI attribute.
+  AtomicString ns = element.namespaceURI();
+
+  // 11. If inherited ns is equal to ns, then:
+  if (inherited_ns == ns && element.prefix().IsEmpty()) {
+    // 11.1. If local default namespace is not null, then set ignore namespace
+    // definition attribute to true.
+    ignore_namespace_definition_attribute = !local_default_namespace.IsNull();
+    // 11.3. Otherwise, append to qualified name the value of node's
+    // localName. The node's prefix if it exists, is dropped.
+    // TODO(tkent): Implement prefix-rewriting for elements.
+
+    // 11.4. Append the value of qualified name to markup.
+    formatter_.AppendStartTagOpen(markup_, element);
+    return ignore_namespace_definition_attribute;
+  }
+
+  if (!element.prefix().IsEmpty()) {
+    formatter_.AppendStartTagOpen(markup_, element);
+    if (ShouldAddNamespaceElement(element))
+      AppendNamespace(element.prefix(), ns);
+    // 12.5.7. If local default namespace is not null (there exists a
+    // locally-defined default namespace declaration attribute), then let
+    // inherited ns get the value of local default namespace unless the local
+    // default namespace is the empty string in which case let it get null.
+    if (local_default_namespace) {
+      namespace_context.SetContextNamespace(local_default_namespace.IsEmpty()
+                                                ? g_null_atom
+                                                : local_default_namespace);
+    }
+    return ignore_namespace_definition_attribute;
+  }
+
+  // 12.6. Otherwise, if local default namespace is null, or local default
+  // namespace is not null and its value is not equal to ns, then:
+  if (local_default_namespace.IsNull() || local_default_namespace != ns) {
+    // 12.6.1. Set the ignore namespace definition attribute flag to true.
+    ignore_namespace_definition_attribute = true;
+    // 12.6.3. Let the value of inherited ns be ns.
+    namespace_context.SetContextNamespace(ns);
+    // 12.6.4. Append the value of qualified name to markup.
+    formatter_.AppendStartTagOpen(markup_, element);
+    // 12.6.5. Append the following to markup, in the order listed:
+    MarkupFormatter::AppendAttribute(markup_, g_null_atom, g_xmlns_atom, ns,
+                                     false);
+    return ignore_namespace_definition_attribute;
+  }
+
+  // 12.7. Otherwise, the node has a local default namespace that matches
+  // ns. Append to qualified name the value of node's localName, let the value
+  // of inherited ns be ns, and append the value of qualified name to markup.
+  DCHECK_EQ(local_default_namespace, ns);
+  namespace_context.SetContextNamespace(ns);
+  formatter_.AppendStartTagOpen(markup_, element);
+  return ignore_namespace_definition_attribute;
 }
 
 void MarkupAccumulator::AppendStartTagClose(const Element& element) {
@@ -277,16 +397,6 @@
   namespace_stack_.pop_back();
 }
 
-// https://w3c.github.io/DOM-Parsing/#dfn-recording-the-namespace-information
-void MarkupAccumulator::RecordNamespaceInformation(const Element& element) {
-  if (SerializeAsHTMLDocument(element))
-    return;
-  for (const auto& attr : element.Attributes()) {
-    if (attr.NamespaceURI() == xmlns_names::kNamespaceURI)
-      AddPrefix(attr.Prefix() ? attr.LocalName() : g_empty_atom, attr.Value());
-  }
-}
-
 // https://w3c.github.io/DOM-Parsing/#dfn-retrieving-a-preferred-prefix-string
 AtomicString MarkupAccumulator::RetrievePreferredPrefixString(
     const AtomicString& ns,
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.h b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.h
index 97d5772..e03c996 100644
--- a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.h
+++ b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.h
@@ -67,7 +67,9 @@
   void AppendString(const String&);
   // Serialize a Node, without its children and its end tag.
   void AppendStartMarkup(const Node&);
-  void AppendStartTagOpen(const Element&);
+  // Returns 'ignore namespace definition attribute' flag.
+  // If it's true, we should not serialize xmlns="..." on the element.
+  bool AppendStartTagOpen(const Element&);
   void AppendStartTagClose(const Element&);
   bool ShouldAddNamespaceElement(const Element&);
   void AppendNamespace(const AtomicString& prefix,
diff --git a/third_party/blink/renderer/core/events/progress_event.cc b/third_party/blink/renderer/core/events/progress_event.cc
index 12ecc576..ccfdd946 100644
--- a/third_party/blink/renderer/core/events/progress_event.cc
+++ b/third_party/blink/renderer/core/events/progress_event.cc
@@ -41,8 +41,8 @@
 
 ProgressEvent::ProgressEvent(const AtomicString& type,
                              bool length_computable,
-                             unsigned long long loaded,
-                             unsigned long long total)
+                             uint64_t loaded,
+                             uint64_t total)
     : Event(type, Bubbles::kNo, Cancelable::kNo),
       length_computable_(length_computable),
       loaded_(loaded),
diff --git a/third_party/blink/renderer/core/events/progress_event.h b/third_party/blink/renderer/core/events/progress_event.h
index ca1119f7..a9fa01d 100644
--- a/third_party/blink/renderer/core/events/progress_event.h
+++ b/third_party/blink/renderer/core/events/progress_event.h
@@ -41,8 +41,8 @@
   }
   static ProgressEvent* Create(const AtomicString& type,
                                bool length_computable,
-                               unsigned long long loaded,
-                               unsigned long long total) {
+                               uint64_t loaded,
+                               uint64_t total) {
     return MakeGarbageCollected<ProgressEvent>(type, length_computable, loaded,
                                                total);
   }
@@ -54,13 +54,13 @@
   ProgressEvent();
   ProgressEvent(const AtomicString& type,
                 bool length_computable,
-                unsigned long long loaded,
-                unsigned long long total);
+                uint64_t loaded,
+                uint64_t total);
   ProgressEvent(const AtomicString&, const ProgressEventInit*);
 
   bool lengthComputable() const { return length_computable_; }
-  unsigned long long loaded() const { return loaded_; }
-  unsigned long long total() const { return total_; }
+  uint64_t loaded() const { return loaded_; }
+  uint64_t total() const { return total_; }
 
   const AtomicString& InterfaceName() const override;
 
@@ -68,8 +68,8 @@
 
  private:
   bool length_computable_;
-  unsigned long long loaded_;
-  unsigned long long total_;
+  uint64_t loaded_;
+  uint64_t total_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/events/resource_progress_event.cc b/third_party/blink/renderer/core/events/resource_progress_event.cc
index 59599bad..e8bdf20 100644
--- a/third_party/blink/renderer/core/events/resource_progress_event.cc
+++ b/third_party/blink/renderer/core/events/resource_progress_event.cc
@@ -32,8 +32,8 @@
 
 ResourceProgressEvent::ResourceProgressEvent(const AtomicString& type,
                                              bool length_computable,
-                                             unsigned long long loaded,
-                                             unsigned long long total,
+                                             uint64_t loaded,
+                                             uint64_t total,
                                              const String& url)
     : ProgressEvent(type, length_computable, loaded, total), url_(url) {}
 
diff --git a/third_party/blink/renderer/core/events/resource_progress_event.h b/third_party/blink/renderer/core/events/resource_progress_event.h
index f38c1a6..d18ff4f 100644
--- a/third_party/blink/renderer/core/events/resource_progress_event.h
+++ b/third_party/blink/renderer/core/events/resource_progress_event.h
@@ -49,8 +49,8 @@
  public:
   static ResourceProgressEvent* Create(const AtomicString& type,
                                        bool length_computable,
-                                       unsigned long long loaded,
-                                       unsigned long long total,
+                                       uint64_t loaded,
+                                       uint64_t total,
                                        const String& url) {
     return MakeGarbageCollected<ResourceProgressEvent>(type, length_computable,
                                                        loaded, total, url);
@@ -58,8 +58,8 @@
 
   ResourceProgressEvent(const AtomicString& type,
                         bool length_computable,
-                        unsigned long long loaded,
-                        unsigned long long total,
+                        uint64_t loaded,
+                        uint64_t total,
                         const String& url);
 
   const String& url() const;
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 7f649128..38d835a5 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2610,7 +2610,7 @@
     }
 
     // TODO(sataya.m):Main frame doesn't create RootFrameViewport in some
-    // webkit_unit_tests (http://crbug.com/644788).
+    // blink_unittests (http://crbug.com/644788).
     if (viewport_scrollable_area_) {
       if (GraphicsLayer* layer_for_horizontal_scrollbar =
               viewport_scrollable_area_->LayerForHorizontalScrollbar()) {
diff --git a/third_party/blink/renderer/core/html/custom/README.md b/third_party/blink/renderer/core/html/custom/README.md
index 74d4f3bf..144c4b34 100644
--- a/third_party/blink/renderer/core/html/custom/README.md
+++ b/third_party/blink/renderer/core/html/custom/README.md
@@ -75,10 +75,10 @@
 ###### C++ Unit Tests
 
 These are in third_party/blink/renderer/core/dom/*_test.cc and are
-built as part of the webkit_unit_tests target. The test names start
+built as part of the blink_unittests target. The test names start
 with CustomElement so you can run them with:
 
-    $ out/Debug/webkit_unit_tests --gtest_filter=CustomElement*
+    $ out/Debug/blink_unittests --gtest_filter=CustomElement*
 
 ###### Web Tests
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
index fa14b7d..95ab1b98 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
@@ -350,12 +350,10 @@
   }
 }
 
-void InspectorEmulationAgent::WillSendRequest(
+void InspectorEmulationAgent::PrepareRequest(
     ExecutionContext* execution_context,
-    unsigned long identifier,
     DocumentLoader* loader,
     ResourceRequest& request,
-    const ResourceResponse& redirect_response,
     const FetchInitiatorInfo& initiator_info,
     ResourceType resource_type) {
   if (!accept_language_override_.Get().IsEmpty() &&
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
index baa03bd3..0adea50e 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
@@ -19,7 +19,6 @@
 class DocumentLoader;
 class ExecutionContext;
 class ResourceRequest;
-class ResourceResponse;
 class WebLocalFrameImpl;
 class WebViewImpl;
 enum class ResourceType : uint8_t;
@@ -82,13 +81,11 @@
   void ApplyAcceptLanguageOverride(String* accept_lang);
   void ApplyUserAgentOverride(String* user_agent);
   void FrameStartedLoading(LocalFrame*);
-  void WillSendRequest(ExecutionContext*,
-                       unsigned long identifier,
-                       DocumentLoader*,
-                       ResourceRequest&,
-                       const ResourceResponse& redirect_response,
-                       const FetchInitiatorInfo&,
-                       ResourceType);
+  void PrepareRequest(ExecutionContext*,
+                      DocumentLoader*,
+                      ResourceRequest&,
+                      const FetchInitiatorInfo&,
+                      ResourceType);
 
   // InspectorBaseAgent overrides.
   protocol::Response disable() override;
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index a530540..26a5180 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -845,12 +845,10 @@
                                    InspectorPageAgent::kDocumentResource);
 }
 
-void InspectorNetworkAgent::WillSendRequest(
+void InspectorNetworkAgent::PrepareRequest(
     ExecutionContext* execution_context,
-    unsigned long identifier,
     DocumentLoader* loader,
     ResourceRequest& request,
-    const ResourceResponse& redirect_response,
     const FetchInitiatorInfo& initiator_info,
     ResourceType resource_type) {
   // Ignore the request initiated internally.
@@ -891,6 +889,19 @@
   }
   if (bypass_service_worker_.Get())
     request.SetSkipServiceWorker(true);
+}
+
+void InspectorNetworkAgent::WillSendRequest(
+    ExecutionContext* execution_context,
+    unsigned long identifier,
+    DocumentLoader* loader,
+    const ResourceRequest& request,
+    const ResourceResponse& redirect_response,
+    const FetchInitiatorInfo& initiator_info,
+    ResourceType resource_type) {
+  // Ignore the request initiated internally.
+  if (initiator_info.name == fetch_initiator_type_names::kInternal)
+    return;
 
   InspectorPageAgent::ResourceType type =
       InspectorPageAgent::ToResourceType(resource_type);
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.h b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
index 6cc00cd..0c316a4 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
@@ -94,10 +94,15 @@
   void DidChangeResourcePriority(DocumentLoader*,
                                  unsigned long identifier,
                                  ResourceLoadPriority);
+  void PrepareRequest(ExecutionContext*,
+                      DocumentLoader*,
+                      ResourceRequest&,
+                      const FetchInitiatorInfo&,
+                      ResourceType);
   void WillSendRequest(ExecutionContext*,
                        unsigned long identifier,
                        DocumentLoader*,
-                       ResourceRequest&,
+                       const ResourceRequest&,
                        const ResourceResponse& redirect_response,
                        const FetchInitiatorInfo&,
                        ResourceType);
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index 82a88ad..55f058c 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -105,7 +105,7 @@
     ExecutionContext*,
     unsigned long identifier,
     DocumentLoader* loader,
-    ResourceRequest& request,
+    const ResourceRequest& request,
     const ResourceResponse& redirect_response,
     const FetchInitiatorInfo&,
     ResourceType) {
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.h b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
index 726ebad..0d766d2 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.h
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
@@ -86,7 +86,7 @@
   void WillSendRequest(ExecutionContext*,
                        unsigned long identifier,
                        DocumentLoader*,
-                       ResourceRequest&,
+                       const ResourceRequest&,
                        const ResourceResponse& redirect_response,
                        const FetchInitiatorInfo&,
                        ResourceType);
diff --git a/third_party/blink/renderer/core/inspector/v8_inspector_string.cc b/third_party/blink/renderer/core/inspector/v8_inspector_string.cc
index d05cd38..1738fed 100644
--- a/third_party/blink/renderer/core/inspector/v8_inspector_string.cc
+++ b/third_party/blink/renderer/core/inspector/v8_inspector_string.cc
@@ -158,6 +158,13 @@
 }
 
 // static
+Binary Binary::fromSpan(const uint8_t* data, size_t size) {
+  Vector<uint8_t> in;
+  in.Append(data, size);
+  return Binary::fromVector(std::move(in));
+}
+
+// static
 Binary Binary::fromCachedData(
     std::unique_ptr<v8::ScriptCompiler::CachedData> data) {
   CHECK_EQ(data->buffer_policy, v8::ScriptCompiler::CachedData::BufferOwned);
diff --git a/third_party/blink/renderer/core/inspector/v8_inspector_string.h b/third_party/blink/renderer/core/inspector/v8_inspector_string.h
index 6b738eb1..483d07b 100644
--- a/third_party/blink/renderer/core/inspector/v8_inspector_string.h
+++ b/third_party/blink/renderer/core/inspector/v8_inspector_string.h
@@ -113,6 +113,7 @@
   static Binary fromBase64(const String& base64, bool* success);
   static Binary fromSharedBuffer(scoped_refptr<SharedBuffer> buffer);
   static Binary fromVector(Vector<uint8_t> in);
+  static Binary fromSpan(const uint8_t* data, size_t size);
 
   // Note: |data.buffer_policy| must be
   // ScriptCompiler::ScriptCompiler::CachedData::BufferOwned.
diff --git a/third_party/blink/renderer/core/layout/ng/README.md b/third_party/blink/renderer/core/layout/ng/README.md
index 08825d0..09c24da 100644
--- a/third_party/blink/renderer/core/layout/ng/README.md
+++ b/third_party/blink/renderer/core/layout/ng/README.md
@@ -106,9 +106,9 @@
 
 #### Generating code coverage ####
 * Build the unit tets target with debug information
-`chromium\src> ninja -C out\Debug webkit_unit_tests`
+`chromium\src> ninja -C out\Debug blink_unittests`
 * Run DynamoRIO with drcov tool
-`chromium\src>DynamoRIO\bin64\drrun.exe -t drcov -- .\out\Debug\webkit_unit_tests.exe --gtest_filter=NG*`
+`chromium\src>DynamoRIO\bin64\drrun.exe -t drcov -- .\out\Debug\blink_unittests.exe --gtest_filter=NG*`
 * Convert the output information to lcov format
 `chromium\src>for %file in (*.log) do DynamoRIO\tools\bin64\drcov2lcov.exe -input %file -output %file.info -src_filter layout/ng -src_skip_filter _test`
 * Merge all lcov files into one file
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
index 904ddc7c..402bc46 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
@@ -193,6 +193,8 @@
 NGPhysicalOffsetRect NGPhysicalTextFragment::LocalRect(
     unsigned start_offset,
     unsigned end_offset) const {
+  if (start_offset == start_offset_ && end_offset == end_offset_)
+    return LocalRect();
   LayoutUnit start_position, end_position;
   std::tie(start_position, end_position) =
       LineLeftAndRightForOffsets(start_offset, end_offset);
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
index 4bbc0b8..7269fca 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -401,8 +401,10 @@
 
 void FrameFetchContext::PrepareRequest(
     ResourceRequest& request,
+    const FetchInitiatorInfo& initiator_info,
     WebScopedVirtualTimePauser& virtual_time_pauser,
-    RedirectType redirect_type) {
+    RedirectType redirect_type,
+    ResourceType resource_type) {
   // TODO(yhirano): Clarify which statements are actually needed when
   // |redirect_type| is |kForRedirect|.
 
@@ -423,6 +425,9 @@
         WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
   }
 
+  probe::prepareRequest(GetFrame()->GetDocument(), MasterDocumentLoader(),
+                        request, initiator_info, resource_type);
+
   // ServiceWorker hook ups.
   if (MasterDocumentLoader()->GetServiceWorkerNetworkProvider()) {
     WrappedResourceRequest webreq(request);
@@ -440,7 +445,7 @@
 
 void FrameFetchContext::DispatchWillSendRequest(
     unsigned long identifier,
-    ResourceRequest& request,
+    const ResourceRequest& request,
     const ResourceResponse& redirect_response,
     ResourceType resource_type,
     const FetchInitiatorInfo& initiator_info) {
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.h b/third_party/blink/renderer/core/loader/frame_fetch_context.h
index 17f6896..b7563821 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.h
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.h
@@ -88,11 +88,13 @@
                                          ResourceLoadPriority,
                                          int intra_priority_value) override;
   void PrepareRequest(ResourceRequest&,
+                      const FetchInitiatorInfo&,
                       WebScopedVirtualTimePauser&,
-                      RedirectType) override;
+                      RedirectType,
+                      ResourceType) override;
   void DispatchWillSendRequest(
       unsigned long identifier,
-      ResourceRequest&,
+      const ResourceRequest&,
       const ResourceResponse& redirect_response,
       ResourceType,
       const FetchInitiatorInfo& = FetchInitiatorInfo()) override;
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
index 5dc01e34..b2f9d7d 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
@@ -1175,8 +1175,8 @@
   ResourceRequest request(KURL("https://localhost/"));
   WebScopedVirtualTimePauser virtual_time_pauser;
   GetFetchContext()->PrepareRequest(
-      request, virtual_time_pauser,
-      FetchContext::RedirectType::kNotForRedirect);
+      request, FetchInitiatorInfo(), virtual_time_pauser,
+      FetchContext::RedirectType::kNotForRedirect, ResourceType::kRaw);
 
   EXPECT_EQ("hi", request.HttpHeaderField(http_names::kUserAgent));
 }
diff --git a/third_party/blink/renderer/core/loader/resource/font_resource.h b/third_party/blink/renderer/core/loader/resource/font_resource.h
index d07e856..d5f2b7cc 100644
--- a/third_party/blink/renderer/core/loader/resource/font_resource.h
+++ b/third_party/blink/renderer/core/loader/resource/font_resource.h
@@ -106,7 +106,7 @@
   TaskHandle font_load_long_limit_;
 
   friend class MemoryCache;
-  FRIEND_TEST_ALL_PREFIXES(CacheAwareFontResourceTest, CacheAwareFontLoading);
+  FRIEND_TEST_ALL_PREFIXES(FontResourceTest, CacheAwareFontLoading);
 };
 
 DEFINE_RESOURCE_TYPE_CASTS(Font);
diff --git a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
index 940c8db..3c8d6ed 100644
--- a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
@@ -4,9 +4,7 @@
 
 #include "third_party/blink/renderer/core/loader/resource/font_resource.h"
 
-#include "base/test/scoped_feature_list.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-shared.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
@@ -39,18 +37,6 @@
   }
 };
 
-class CacheAwareFontResourceTest : public FontResourceTest {
- public:
-  void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kWebFontsCacheAwareTimeoutAdaption);
-    FontResourceTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
 // Tests if ResourceFetcher works fine with FontResource that requires defered
 // loading supports.
 TEST_F(FontResourceTest,
@@ -114,7 +100,7 @@
 }
 
 // Tests if cache-aware font loading works correctly.
-TEST_F(CacheAwareFontResourceTest, CacheAwareFontLoading) {
+TEST_F(FontResourceTest, CacheAwareFontLoading) {
   KURL url("http://127.0.0.1:8000/font.woff");
   ResourceResponse response(url);
   response.SetHTTPStatusCode(200);
diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.cc b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
index 2e7f9c08..6ef9a8a 100644
--- a/third_party/blink/renderer/core/loader/worker_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
@@ -182,9 +182,12 @@
   return global_scope_->AddConsoleMessage(message);
 }
 
-void WorkerFetchContext::PrepareRequest(ResourceRequest& request,
-                                        WebScopedVirtualTimePauser&,
-                                        RedirectType) {
+void WorkerFetchContext::PrepareRequest(
+    ResourceRequest& request,
+    const FetchInitiatorInfo& initiator_info,
+    WebScopedVirtualTimePauser&,
+    RedirectType redirect_type,
+    ResourceType resource_type) {
   String user_agent = global_scope_->UserAgent();
   probe::applyUserAgentOverride(Probe(), &user_agent);
   DCHECK(!user_agent.IsNull());
@@ -192,6 +195,9 @@
 
   WrappedResourceRequest webreq(request);
   web_context_->WillSendRequest(webreq);
+
+  probe::prepareRequest(global_scope_, nullptr, request, initiator_info,
+                        resource_type);
 }
 
 void WorkerFetchContext::AddAdditionalRequestHeaders(ResourceRequest& request) {
@@ -207,7 +213,7 @@
 
 void WorkerFetchContext::DispatchWillSendRequest(
     unsigned long identifier,
-    ResourceRequest& request,
+    const ResourceRequest& request,
     const ResourceResponse& redirect_response,
     ResourceType resource_type,
     const FetchInitiatorInfo& initiator_info) {
diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.h b/third_party/blink/renderer/core/loader/worker_fetch_context.h
index 67562d0..89570f2 100644
--- a/third_party/blink/renderer/core/loader/worker_fetch_context.h
+++ b/third_party/blink/renderer/core/loader/worker_fetch_context.h
@@ -68,11 +68,13 @@
 
   // FetchContext implementation:
   void PrepareRequest(ResourceRequest&,
+                      const FetchInitiatorInfo&,
                       WebScopedVirtualTimePauser&,
-                      RedirectType) override;
+                      RedirectType,
+                      ResourceType) override;
   void AddAdditionalRequestHeaders(ResourceRequest&) override;
   void DispatchWillSendRequest(unsigned long,
-                               ResourceRequest&,
+                               const ResourceRequest&,
                                const ResourceResponse&,
                                ResourceType,
                                const FetchInitiatorInfo&) override;
diff --git a/third_party/blink/renderer/core/probe/core_probes.json5 b/third_party/blink/renderer/core/probe/core_probes.json5
index b76043c..c779466 100644
--- a/third_party/blink/renderer/core/probe/core_probes.json5
+++ b/third_party/blink/renderer/core/probe/core_probes.json5
@@ -99,7 +99,7 @@
         "applyAcceptLanguageOverride",
         "applyUserAgentOverride",
         "frameStartedLoading",
-        "willSendRequest",
+        "prepareRequest",
       ]
     },
     InspectorLayerTreeAgent: {
@@ -137,6 +137,7 @@
         "frameClearedScheduledNavigation",
         "frameScheduledNavigation",
         "markResourceAsCached",
+        "prepareRequest",
         "scriptImported",
         "shouldBlockRequest",
         "shouldBypassServiceWorker",
diff --git a/third_party/blink/renderer/core/probe/core_probes.pidl b/third_party/blink/renderer/core/probe/core_probes.pidl
index de8a05a..92ca4aa0 100644
--- a/third_party/blink/renderer/core/probe/core_probes.pidl
+++ b/third_party/blink/renderer/core/probe/core_probes.pidl
@@ -91,7 +91,8 @@
   void applyUserAgentOverride(CoreProbeSink*, String* userAgent);
   void didBlockRequest([Keep] ExecutionContext*, const ResourceRequest&, DocumentLoader*, const FetchInitiatorInfo&, ResourceRequestBlockedReason, ResourceType);
   void didChangeResourcePriority(LocalFrame*, DocumentLoader*, unsigned long identifier, ResourceLoadPriority loadPriority);
-  void willSendRequest([Keep] ExecutionContext*, unsigned long identifier, DocumentLoader*, ResourceRequest&, const ResourceResponse& redirectResponse, const FetchInitiatorInfo&, ResourceType);
+  void prepareRequest([Keep] ExecutionContext*, DocumentLoader*, ResourceRequest&, const FetchInitiatorInfo&, ResourceType);
+  void willSendRequest([Keep] ExecutionContext*, unsigned long identifier, DocumentLoader*, const ResourceRequest&, const ResourceResponse& redirectResponse, const FetchInitiatorInfo&, ResourceType);
   void willSendNavigationRequest([Keep] ExecutionContext*, unsigned long identifier, DocumentLoader*, const KURL&, const AtomicString& http_method, EncodedFormData*);
   void markResourceAsCached(LocalFrame*, DocumentLoader*, unsigned long identifier);
   void didReceiveResourceResponse(CoreProbeSink*, unsigned long identifier, DocumentLoader*, const ResourceResponse&, Resource*);
diff --git a/third_party/blink/renderer/core/script/import_map.cc b/third_party/blink/renderer/core/script/import_map.cc
index 4f9da28..35e26d97 100644
--- a/third_party/blink/renderer/core/script/import_map.cc
+++ b/third_party/blink/renderer/core/script/import_map.cc
@@ -53,6 +53,11 @@
       return key.GetImportMapKeyString();
 
     case ParsedSpecifier::Type::kURL:
+      if (!SchemeRegistry::IsFetchScheme(key.GetUrl().Protocol())) {
+        AddIgnoredKeyMessage(logger, key_string,
+                             "Invalid key (non-fetch scheme)");
+        return String();
+      }
       return key.GetImportMapKeyString();
   }
 }
diff --git a/third_party/blink/renderer/devtools/front_end/devtools_app.html b/third_party/blink/renderer/devtools/front_end/devtools_app.html
index 76b4116d..81b6645 100644
--- a/third_party/blink/renderer/devtools/front_end/devtools_app.html
+++ b/third_party/blink/renderer/devtools/front_end/devtools_app.html
@@ -6,11 +6,11 @@
 <!doctype html>
 <html lang="en">
 <head>
-    <meta http-equiv="content-type" content="text/html; charset=utf-8">
+    <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
     <meta name="referrer" content="no-referrer">
-    <script type="text/javascript" src="Runtime.js"></script>
-    <script type="text/javascript" src="devtools_app.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="devtools_app.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/inspector.html b/third_party/blink/renderer/devtools/front_end/inspector.html
index c4da1f5..b5d6f04e 100644
--- a/third_party/blink/renderer/devtools/front_end/inspector.html
+++ b/third_party/blink/renderer/devtools/front_end/inspector.html
@@ -6,11 +6,11 @@
 <!doctype html>
 <html lang="en">
 <head>
-    <meta http-equiv="content-type" content="text/html; charset=utf-8">
+    <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
     <meta name="referrer" content="no-referrer">
-    <script type="text/javascript" src="Runtime.js"></script>
-    <script type="text/javascript" src="inspector.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="inspector.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/integration_test_runner.html b/third_party/blink/renderer/devtools/front_end/integration_test_runner.html
index f982e1be..ca7cc521 100644
--- a/third_party/blink/renderer/devtools/front_end/integration_test_runner.html
+++ b/third_party/blink/renderer/devtools/front_end/integration_test_runner.html
@@ -6,10 +6,10 @@
 <!doctype html>
 <html lang="en">
 <head>
-    <meta http-equiv="content-type" content="text/html; charset=utf-8">
+    <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
-    <script type="text/javascript" src="Runtime.js"></script>
-    <script type="text/javascript" src="integration_test_runner.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="integration_test_runner.js"></script>
 </head>
 <body id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/js_app.html b/third_party/blink/renderer/devtools/front_end/js_app.html
index ef077a4..7105918 100644
--- a/third_party/blink/renderer/devtools/front_end/js_app.html
+++ b/third_party/blink/renderer/devtools/front_end/js_app.html
@@ -6,11 +6,11 @@
 <!doctype html>
 <html lang="en">
 <head>
-    <meta http-equiv="content-type" content="text/html; charset=utf-8">
+    <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
     <meta name="referrer" content="no-referrer">
-    <script type="text/javascript" src="Runtime.js"></script>
-    <script type="text/javascript" src="js_app.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="js_app.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/ndb_app.html b/third_party/blink/renderer/devtools/front_end/ndb_app.html
index aca9d950..ac0dee1 100644
--- a/third_party/blink/renderer/devtools/front_end/ndb_app.html
+++ b/third_party/blink/renderer/devtools/front_end/ndb_app.html
@@ -6,11 +6,11 @@
 <!doctype html>
 <html lang="en">
 <head>
-    <meta http-equiv="content-type" content="text/html; charset=utf-8">
+    <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
     <meta name="referrer" content="no-referrer">
-    <script type="text/javascript" src="Runtime.js"></script>
-    <script type="text/javascript" src="ndb_app.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="ndb_app.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/node_app.html b/third_party/blink/renderer/devtools/front_end/node_app.html
index 71c675c..05aa910 100644
--- a/third_party/blink/renderer/devtools/front_end/node_app.html
+++ b/third_party/blink/renderer/devtools/front_end/node_app.html
@@ -6,11 +6,11 @@
 <!doctype html>
 <html lang="en">
 <head>
-    <meta http-equiv="content-type" content="text/html; charset=utf-8">
+    <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
     <meta name="referrer" content="no-referrer">
-    <script type="text/javascript" src="Runtime.js"></script>
-    <script type="text/javascript" src="node_app.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="node_app.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/toolbox.html b/third_party/blink/renderer/devtools/front_end/toolbox.html
index 267356c..c48ad36 100644
--- a/third_party/blink/renderer/devtools/front_end/toolbox.html
+++ b/third_party/blink/renderer/devtools/front_end/toolbox.html
@@ -6,10 +6,10 @@
 <!doctype html>
 <html lang="en">
 <head>
-    <meta http-equiv="content-type" content="text/html; charset=utf-8">
+    <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' ">
-    <script type="text/javascript" src="Runtime.js"></script>
-    <script type="text/javascript" src="toolbox.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="toolbox.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/worker_app.html b/third_party/blink/renderer/devtools/front_end/worker_app.html
index ad332c56..e5827b5e 100644
--- a/third_party/blink/renderer/devtools/front_end/worker_app.html
+++ b/third_party/blink/renderer/devtools/front_end/worker_app.html
@@ -6,11 +6,11 @@
 <!doctype html>
 <html lang="en">
 <head>
-    <meta http-equiv="content-type" content="text/html; charset=utf-8">
+    <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
     <meta name="referrer" content="no-referrer">
-    <script type="text/javascript" src="Runtime.js"></script>
-    <script type="text/javascript" src="worker_app.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="worker_app.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/modules/eventsource/event_source.cc b/third_party/blink/renderer/modules/eventsource/event_source.cc
index 645f9c2f..0e45e41 100644
--- a/third_party/blink/renderer/modules/eventsource/event_source.cc
+++ b/third_party/blink/renderer/modules/eventsource/event_source.cc
@@ -60,7 +60,7 @@
 
 namespace blink {
 
-const unsigned long long EventSource::kDefaultReconnectDelay = 3000;
+const uint64_t EventSource::kDefaultReconnectDelay = 3000;
 
 inline EventSource::EventSource(ExecutionContext* context,
                                 const KURL& url,
@@ -331,7 +331,7 @@
   DispatchEvent(*e);
 }
 
-void EventSource::OnReconnectionTimeSet(unsigned long long reconnection_time) {
+void EventSource::OnReconnectionTimeSet(uint64_t reconnection_time) {
   reconnect_delay_ = reconnection_time;
 }
 
diff --git a/third_party/blink/renderer/modules/eventsource/event_source.h b/third_party/blink/renderer/modules/eventsource/event_source.h
index 6be9ded..f21e6652 100644
--- a/third_party/blink/renderer/modules/eventsource/event_source.h
+++ b/third_party/blink/renderer/modules/eventsource/event_source.h
@@ -69,7 +69,7 @@
   EventSource(ExecutionContext*, const KURL&, const EventSourceInit*);
   ~EventSource() override;
 
-  static const unsigned long long kDefaultReconnectDelay;
+  static const uint64_t kDefaultReconnectDelay;
 
   String url() const;
   bool withCredentials() const;
@@ -111,7 +111,7 @@
   void OnMessageEvent(const AtomicString& event,
                       const String& data,
                       const AtomicString& id) override;
-  void OnReconnectionTimeSet(unsigned long long reconnection_time) override;
+  void OnReconnectionTimeSet(uint64_t reconnection_time) override;
 
   void ScheduleInitialConnect();
   void Connect();
@@ -133,7 +133,7 @@
   Member<ThreadableLoader> loader_;
   TaskRunnerTimer<EventSource> connect_timer_;
 
-  unsigned long long reconnect_delay_;
+  uint64_t reconnect_delay_;
   String event_stream_origin_;
   unsigned long resource_identifier_ = 0;
 };
diff --git a/third_party/blink/renderer/modules/eventsource/event_source_parser.h b/third_party/blink/renderer/modules/eventsource/event_source_parser.h
index e3eb4e0..7596ab2 100644
--- a/third_party/blink/renderer/modules/eventsource/event_source_parser.h
+++ b/third_party/blink/renderer/modules/eventsource/event_source_parser.h
@@ -24,8 +24,7 @@
     virtual void OnMessageEvent(const AtomicString& type,
                                 const String& data,
                                 const AtomicString& last_event_id) = 0;
-    virtual void OnReconnectionTimeSet(
-        unsigned long long reconnection_time) = 0;
+    virtual void OnReconnectionTimeSet(uint64_t reconnection_time) = 0;
     void Trace(blink::Visitor* visitor) override {}
   };
 
diff --git a/third_party/blink/renderer/modules/eventsource/event_source_parser_test.cc b/third_party/blink/renderer/modules/eventsource/event_source_parser_test.cc
index a9f33d9..f7ce421b 100644
--- a/third_party/blink/renderer/modules/eventsource/event_source_parser_test.cc
+++ b/third_party/blink/renderer/modules/eventsource/event_source_parser_test.cc
@@ -25,14 +25,14 @@
                                  const String& data,
                                  const AtomicString& id)
       : type(kEvent), event(event), data(data), id(id), reconnection_time(0) {}
-  explicit EventOrReconnectionTimeSetting(unsigned long long reconnection_time)
+  explicit EventOrReconnectionTimeSetting(uint64_t reconnection_time)
       : type(kReconnectionTimeSetting), reconnection_time(reconnection_time) {}
 
   const Type type;
   const AtomicString event;
   const String data;
   const AtomicString id;
-  const unsigned long long reconnection_time;
+  const uint64_t reconnection_time;
 };
 
 class Client : public GarbageCollectedFinalized<Client>,
@@ -49,7 +49,7 @@
                       const AtomicString& id) override {
     events_.push_back(EventOrReconnectionTimeSetting(event, data, id));
   }
-  void OnReconnectionTimeSet(unsigned long long reconnection_time) override {
+  void OnReconnectionTimeSet(uint64_t reconnection_time) override {
     events_.push_back(EventOrReconnectionTimeSetting(reconnection_time));
   }
 
@@ -73,7 +73,7 @@
     parser_->Stop();
     events_.push_back(EventOrReconnectionTimeSetting(event, data, id));
   }
-  void OnReconnectionTimeSet(unsigned long long reconnection_time) override {
+  void OnReconnectionTimeSet(uint64_t reconnection_time) override {
     events_.push_back(EventOrReconnectionTimeSetting(reconnection_time));
   }
 
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation.cc b/third_party/blink/renderer/modules/geolocation/geolocation.cc
index 99df8bd1..deb344d 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation.cc
+++ b/third_party/blink/renderer/modules/geolocation/geolocation.cc
@@ -466,7 +466,7 @@
   GetFrame()->GetInterfaceProvider().GetInterface(&geolocation_service_,
                                                   invalidator, task_runner);
   geolocation_service_->CreateGeolocation(
-      MakeRequest(&geolocation_, invalidator),
+      MakeRequest(&geolocation_, invalidator, std::move(task_runner)),
       LocalFrame::HasTransientUserActivation(GetFrame()));
 
   geolocation_.set_connection_error_handler(WTF::Bind(
diff --git a/third_party/blink/renderer/modules/quota/deprecated_storage_info.cc b/third_party/blink/renderer/modules/quota/deprecated_storage_info.cc
index 47152d2..4be8a8c 100644
--- a/third_party/blink/renderer/modules/quota/deprecated_storage_info.cc
+++ b/third_party/blink/renderer/modules/quota/deprecated_storage_info.cc
@@ -61,7 +61,7 @@
 void DeprecatedStorageInfo::requestQuota(
     ScriptState* script_state,
     int storage_type,
-    unsigned long long new_quota_in_bytes,
+    uint64_t new_quota_in_bytes,
     V8StorageQuotaCallback* success_callback,
     V8StorageErrorCallback* error_callback) {
   // Dispatching the request to DeprecatedStorageQuota, as this interface is
diff --git a/third_party/blink/renderer/modules/quota/deprecated_storage_info.h b/third_party/blink/renderer/modules/quota/deprecated_storage_info.h
index a52c318..17352c5 100644
--- a/third_party/blink/renderer/modules/quota/deprecated_storage_info.h
+++ b/third_party/blink/renderer/modules/quota/deprecated_storage_info.h
@@ -65,7 +65,7 @@
 
   void requestQuota(ScriptState*,
                     int storage_type,
-                    unsigned long long new_quota_in_bytes,
+                    uint64_t new_quota_in_bytes,
                     V8StorageQuotaCallback* = nullptr,
                     V8StorageErrorCallback* = nullptr);
 
diff --git a/third_party/blink/renderer/modules/quota/deprecated_storage_quota.cc b/third_party/blink/renderer/modules/quota/deprecated_storage_quota.cc
index f2f784b..33be07a 100644
--- a/third_party/blink/renderer/modules/quota/deprecated_storage_quota.cc
+++ b/third_party/blink/renderer/modules/quota/deprecated_storage_quota.cc
@@ -166,7 +166,7 @@
 
 void DeprecatedStorageQuota::requestQuota(
     ScriptState* script_state,
-    unsigned long long new_quota_in_bytes,
+    uint64_t new_quota_in_bytes,
     V8StorageQuotaCallback* success_callback,
     V8StorageErrorCallback* error_callback) {
   ExecutionContext& execution_context = *ExecutionContext::From(script_state);
diff --git a/third_party/blink/renderer/modules/quota/deprecated_storage_quota.h b/third_party/blink/renderer/modules/quota/deprecated_storage_quota.h
index 4903732..56dff84 100644
--- a/third_party/blink/renderer/modules/quota/deprecated_storage_quota.h
+++ b/third_party/blink/renderer/modules/quota/deprecated_storage_quota.h
@@ -68,7 +68,7 @@
                           V8StorageErrorCallback* = nullptr);
 
   void requestQuota(ScriptState*,
-                    unsigned long long new_quota_in_bytes,
+                    uint64_t new_quota_in_bytes,
                     V8StorageQuotaCallback* = nullptr,
                     V8StorageErrorCallback* = nullptr);
 
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 0957399..048cd544 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -2105,7 +2105,7 @@
 
 # NOTE: These are legacy unit tests and tests that require a Platform
 # object. Do not add more unless the test requires a Platform object.
-# These tests are a part of the webkit_unit_tests binary.
+# These tests are a part of the blink_unittests binary.
 jumbo_source_set("unit_tests") {
   testonly = true
   visibility = []
diff --git a/third_party/blink/renderer/platform/blob/BUILD.gn b/third_party/blink/renderer/platform/blob/BUILD.gn
index b40abbe..5a40d451 100644
--- a/third_party/blink/renderer/platform/blob/BUILD.gn
+++ b/third_party/blink/renderer/platform/blob/BUILD.gn
@@ -59,12 +59,12 @@
 
 jumbo_source_set("test_support") {
   # This target defines test files for platform:test_support that
-  # blink_platform_unittests and webkit_unit_tests can use.
+  # blink_platform_unittests and blink_unittests can use.
   visibility = [ "//third_party/blink/renderer/platform:test_support" ]
   testonly = true
 
   # Source files that can be called from blink_platform_unittests and
-  # webkit_unit_tests.
+  # blink_unittests.
   sources = [
     "testing/fake_blob.cc",
     "testing/fake_blob.h",
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
index 96036bb..1a0b606 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
@@ -9,7 +9,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/trace_event/trace_event.h"
 #include "components/viz/client/client_resource_provider.h"
-#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/gpu/raster_context_provider.h"
 #include "components/viz/common/quads/render_pass.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
@@ -32,7 +32,7 @@
 }
 
 void VideoFrameResourceProvider::Initialize(
-    viz::ContextProvider* media_context_provider,
+    viz::RasterContextProvider* media_context_provider,
     viz::SharedBitmapReporter* shared_bitmap_reporter) {
   context_provider_ = media_context_provider;
   resource_provider_ = std::make_unique<viz::ClientResourceProvider>(
@@ -48,8 +48,8 @@
   }
 
   resource_updater_ = std::make_unique<media::VideoResourceUpdater>(
-      media_context_provider, shared_bitmap_reporter, resource_provider_.get(),
-      settings_.use_stream_video_draw_quad,
+      nullptr, media_context_provider, shared_bitmap_reporter,
+      resource_provider_.get(), settings_.use_stream_video_draw_quad,
       settings_.resource_settings.use_gpu_memory_buffer_resources,
       settings_.resource_settings.use_r16_texture, max_texture_size);
 }
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h
index 709958e..e4e5d2a3 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h
+++ b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h
@@ -19,6 +19,7 @@
 
 namespace viz {
 class RenderPass;
+class RasterContextProvider;
 }
 
 namespace blink {
@@ -37,7 +38,8 @@
 
   virtual ~VideoFrameResourceProvider();
 
-  virtual void Initialize(viz::ContextProvider*, viz::SharedBitmapReporter*);
+  virtual void Initialize(viz::RasterContextProvider* media_context_provider,
+                          viz::SharedBitmapReporter* shared_bitmap_reporter);
   virtual void AppendQuads(viz::RenderPass*,
                            scoped_refptr<media::VideoFrame>,
                            media::VideoRotation,
@@ -59,7 +61,7 @@
  private:
   const cc::LayerTreeSettings settings_;
 
-  viz::ContextProvider* context_provider_;
+  viz::RasterContextProvider* context_provider_;
   std::unique_ptr<viz::ClientResourceProvider> resource_provider_;
   std::unique_ptr<media::VideoResourceUpdater> resource_updater_;
   bool use_sync_primitives_ = false;
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
index 328417c6..1111ff9 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -242,7 +242,7 @@
 
 void VideoFrameSubmitter::OnReceivedContextProvider(
     bool use_gpu_compositing,
-    scoped_refptr<viz::ContextProvider> context_provider) {
+    scoped_refptr<viz::RasterContextProvider> context_provider) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (!use_gpu_compositing) {
     resource_provider_->Initialize(nullptr, this);
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.h b/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
index 2a368459..75ef619 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
@@ -86,7 +86,7 @@
   // requested.
   void OnReceivedContextProvider(
       bool use_gpu_compositing,
-      scoped_refptr<viz::ContextProvider> context_provider);
+      scoped_refptr<viz::RasterContextProvider> context_provider);
 
   // Starts submission and calls UpdateSubmissionState(); which may submit.
   void StartSubmitting();
@@ -124,7 +124,7 @@
       scoped_refptr<media::VideoFrame> video_frame);
 
   cc::VideoFrameProvider* video_frame_provider_ = nullptr;
-  scoped_refptr<viz::ContextProvider> context_provider_;
+  scoped_refptr<viz::RasterContextProvider> context_provider_;
   viz::mojom::blink::CompositorFrameSinkPtr compositor_frame_sink_;
   mojom::blink::SurfaceEmbedderPtr surface_embedder_;
   mojo::Binding<viz::mojom::blink::CompositorFrameSinkClient> binding_;
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
index b4bf833..cb7105e 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_tick_clock.h"
@@ -113,7 +114,7 @@
     : public blink::VideoFrameResourceProvider {
  public:
   MockVideoFrameResourceProvider(
-      viz::ContextProvider* context_provider,
+      viz::RasterContextProvider* context_provider,
       viz::SharedBitmapReporter* shared_bitmap_reporter)
       : blink::VideoFrameResourceProvider(cc::LayerTreeSettings(), false) {
     blink::VideoFrameResourceProvider::Initialize(context_provider,
@@ -122,7 +123,7 @@
   ~MockVideoFrameResourceProvider() override = default;
 
   MOCK_METHOD2(Initialize,
-               void(viz::ContextProvider*, viz::SharedBitmapReporter*));
+               void(viz::RasterContextProvider*, viz::SharedBitmapReporter*));
   MOCK_METHOD4(AppendQuads,
                void(viz::RenderPass*,
                     scoped_refptr<media::VideoFrame>,
@@ -158,10 +159,7 @@
     resource_provider_ = new StrictMock<MockVideoFrameResourceProvider>(
         context_provider_.get(), nullptr);
     submitter_ = std::make_unique<VideoFrameSubmitter>(
-        base::BindRepeating(
-            [](scoped_refptr<viz::ContextProvider>,
-               base::OnceCallback<void(
-                   bool, scoped_refptr<viz::ContextProvider>)>) {}),
+        base::DoNothing(),
         base::WrapUnique<MockVideoFrameResourceProvider>(resource_provider_));
 
     submitter_->Initialize(video_frame_provider_.get());
@@ -207,7 +205,7 @@
 
   void OnReceivedContextProvider(
       bool use_gpu_compositing,
-      scoped_refptr<viz::ContextProvider> context_provider) {
+      scoped_refptr<viz::RasterContextProvider> context_provider) {
     submitter_->OnReceivedContextProvider(use_gpu_compositing,
                                           std::move(context_provider));
   }
diff --git a/third_party/blink/renderer/platform/loader/BUILD.gn b/third_party/blink/renderer/platform/loader/BUILD.gn
index 989a8e4..92c75f14 100644
--- a/third_party/blink/renderer/platform/loader/BUILD.gn
+++ b/third_party/blink/renderer/platform/loader/BUILD.gn
@@ -165,12 +165,12 @@
 
 jumbo_source_set("test_support") {
   # This target defines test files for platform:test_support that
-  # blink_platform_unittests and webkit_unit_tests can use.
+  # blink_platform_unittests and blink_unittests can use.
   visibility = [ "//third_party/blink/renderer/platform:test_support" ]
   testonly = true
 
   # Source files that can be called from blink_platform_unittests and
-  # webkit_unit_tests.
+  # blink_unittests.
   sources = [
     "testing/bytes_consumer_test_reader.cc",
     "testing/bytes_consumer_test_reader.h",
diff --git a/third_party/blink/renderer/platform/loader/README.md b/third_party/blink/renderer/platform/loader/README.md
index 83fa16c..af6f194 100644
--- a/third_party/blink/renderer/platform/loader/README.md
+++ b/third_party/blink/renderer/platform/loader/README.md
@@ -18,5 +18,5 @@
 ## testing
 
 Contains helper files for testing that are available in both
-`blink_platform_unittests` and `webkit_unit_tests`.
+`blink_platform_unittests` and `blink_unittests`.
 These files are built as a part of the `platform:test_support` static library.
diff --git a/third_party/blink/renderer/platform/loader/cors/cors.cc b/third_party/blink/renderer/platform/loader/cors/cors.cc
index 6b53740..f68005b 100644
--- a/third_party/blink/renderer/platform/loader/cors/cors.cc
+++ b/third_party/blink/renderer/platform/loader/cors/cors.cc
@@ -94,6 +94,11 @@
     while (true) {
       ConsumeSpaces();
 
+      if (pos_ == value_.length() && !output.empty()) {
+        output.insert(std::string());
+        return;
+      }
+
       size_t token_start = pos_;
       ConsumeTokenChars();
       size_t token_size = pos_ - token_start;
diff --git a/third_party/blink/renderer/platform/loader/cors/cors_test.cc b/third_party/blink/renderer/platform/loader/cors/cors_test.cc
index f6c652e..9bf6b13 100644
--- a/third_party/blink/renderer/platform/loader/cors/cors_test.cc
+++ b/third_party/blink/renderer/platform/loader/cors/cors_test.cc
@@ -35,6 +35,8 @@
 
   EXPECT_EQ(Parse(CredentialsMode::kOmit, " \t   \t\t a"),
             WebHTTPHeaderSet({"a"}));
+
+  EXPECT_EQ(Parse(CredentialsMode::kOmit, "a , "), WebHTTPHeaderSet({"a", ""}));
 }
 
 TEST_F(CorsExposedHeadersTest, DuplicatedEntries) {
@@ -57,8 +59,6 @@
 
   EXPECT_TRUE(Parse(CredentialsMode::kOmit, " , a").empty());
 
-  EXPECT_TRUE(Parse(CredentialsMode::kOmit, "a , ").empty());
-
   EXPECT_TRUE(Parse(CredentialsMode::kOmit, "").empty());
 
   EXPECT_TRUE(Parse(CredentialsMode::kOmit, " ").empty());
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc b/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
index 7a57332..5d59942 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
@@ -79,11 +79,13 @@
 }
 
 void FetchContext::PrepareRequest(ResourceRequest&,
+                                  const FetchInitiatorInfo&,
                                   WebScopedVirtualTimePauser&,
-                                  RedirectType) {}
+                                  RedirectType,
+                                  ResourceType) {}
 
 void FetchContext::DispatchWillSendRequest(unsigned long,
-                                           ResourceRequest&,
+                                           const ResourceRequest&,
                                            const ResourceResponse&,
                                            ResourceType,
                                            const FetchInitiatorInfo&) {}
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
index 3fe67b4..8819c05 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
@@ -121,16 +121,19 @@
   // |virtual_time_pauser| is an output parameter. PrepareRequest may
   // create a new WebScopedVirtualTimePauser and set it to
   // |virtual_time_pauser|.
+  // This is called on initial and every redirect request.
   enum class RedirectType { kForRedirect, kNotForRedirect };
   virtual void PrepareRequest(ResourceRequest&,
+                              const FetchInitiatorInfo&,
                               WebScopedVirtualTimePauser& virtual_time_pauser,
-                              RedirectType);
+                              RedirectType,
+                              ResourceType);
 
   // The last callback before a request is actually sent to the browser process.
-  // TODO(https://crbug.com/632580): make this take const ResourceRequest&.
+  // This is called on initial and every redirect request.
   virtual void DispatchWillSendRequest(
       unsigned long identifier,
-      ResourceRequest&,
+      const ResourceRequest&,
       const ResourceResponse& redirect_response,
       ResourceType,
       const FetchInitiatorInfo& = FetchInitiatorInfo());
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index 7bb9bee..5242534e 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -828,8 +828,9 @@
 
   // For initial requests, call PrepareRequest() here before revalidation
   // policy is determined.
-  Context().PrepareRequest(resource_request, virtual_time_pauser,
-                           FetchContext::RedirectType::kNotForRedirect);
+  Context().PrepareRequest(
+      resource_request, options.initiator_info, virtual_time_pauser,
+      FetchContext::RedirectType::kNotForRedirect, resource_type);
 
   if (!params.Url().IsValid())
     return ResourceRequestBlockedReason::kOther;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
index cffd673..19c0c1b3b 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -734,8 +734,10 @@
   // mismatch.
 
   WebScopedVirtualTimePauser unused_virtual_time_pauser;
-  Context().PrepareRequest(*new_request, unused_virtual_time_pauser,
-                           FetchContext::RedirectType::kForRedirect);
+  Context().PrepareRequest(*new_request, resource_->Options().initiator_info,
+                           unused_virtual_time_pauser,
+                           FetchContext::RedirectType::kForRedirect,
+                           resource_->GetType());
   Context().DispatchWillSendRequest(
       resource_->Identifier(), *new_request, redirect_response_to_pass,
       resource_->GetType(), options.initiator_info);
diff --git a/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc b/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc
index 7f41711..88da5377 100644
--- a/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc
@@ -58,7 +58,6 @@
     return parent_->IsServedFromCacheStorage();
   }
 
-  // No memory to report here because it is attributed to |parent_|.
   void OnMemoryDump(WebProcessMemoryDump* pmd,
                     const String& dump_prefix) const override {
     // No memory to report here because it is attributed to |parent_|.
diff --git a/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h b/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h
index 7acce25..e2c4e8df 100644
--- a/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h
+++ b/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h
@@ -42,7 +42,7 @@
   // FetchContext:
   void DispatchWillSendRequest(
       unsigned long identifier,
-      ResourceRequest& request,
+      const ResourceRequest& request,
       const ResourceResponse& redirect_response,
       ResourceType,
       const FetchInitiatorInfo& = FetchInitiatorInfo()) override {
diff --git a/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h b/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h
index 5ba8d5f..978b7a4 100644
--- a/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h
+++ b/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h
@@ -8,7 +8,7 @@
 namespace blink {
 
 // Instantiating BlinkFuzzerTestSupport will spin up an environment similar to
-// webkit_unit_tests. It should be statically initialized and leaked in fuzzers.
+// blink_unittests. It should be statically initialized and leaked in fuzzers.
 class BlinkFuzzerTestSupport {
  public:
   // Use this constructor in LLVMFuzzerTestOneInput.
diff --git a/third_party/blink/renderer/platform/testing/unit_test_helpers.h b/third_party/blink/renderer/platform/testing/unit_test_helpers.h
index 832b5d8..a60ebea 100644
--- a/third_party/blink/renderer/platform/testing/unit_test_helpers.h
+++ b/third_party/blink/renderer/platform/testing/unit_test_helpers.h
@@ -59,20 +59,20 @@
 // Returns directory containing the current executable as absolute path.
 String ExecutableDir();
 
-// Returns test data absolute path for webkit_unit_tests in core, i.e.
-// <blinkRootDir>/Source/core/testing/data/<relativePath>.
-// It returns the top web test directory if |relativePath| was not specified.
+// Returns test data absolute path for blink_unittests in core, i.e.
+// <blinkRootDir>/renderer/core/testing/data/<relative_path>.
+// It returns the top web test directory if |relative_path| was not specified.
 String CoreTestDataPath(const String& relative_path = String());
 
 // Returns test data absolute path for blink_platform_unittests, i.e.
-// <blinkRootDir>/Source/platform/testing/data/<relativePath>.
-// It returns the top platform test directory if |relativePath| was not
+// <blinkRootDir>/renderer/platform/testing/data/<relative_path>.
+// It returns the top platform test directory if |relative_path| was not
 // specified.
 String PlatformTestDataPath(const String& relative_path = String());
 
 // Returns test data absolute path for accessibility unittests, i.e.
-// <blinkRootDir>/renderer/modules/accessibility/testing/data/<relativePath>. It
-// returns the top accessibility test directory if |relativePath| was not
+// <blinkRootDir>/renderer/modules/accessibility/testing/data/<relative_path>.
+// It returns the top accessibility test directory if |relative_path| was not
 // specified.
 String AccessibilityTestDataPath(const String& relative_path = String());
 
diff --git a/third_party/blink/renderer/platform/weborigin/scheme_registry.cc b/third_party/blink/renderer/platform/weborigin/scheme_registry.cc
index 65a0b90..d85aa29 100644
--- a/third_party/blink/renderer/platform/weborigin/scheme_registry.cc
+++ b/third_party/blink/renderer/platform/weborigin/scheme_registry.cc
@@ -293,6 +293,16 @@
   return GetURLSchemesRegistry().fetch_api_schemes.Contains(scheme);
 }
 
+// https://fetch.spec.whatwg.org/#fetch-scheme
+bool SchemeRegistry::IsFetchScheme(const String& scheme) {
+  DCHECK_EQ(scheme, scheme.LowerASCII());
+  // "A fetch scheme is a scheme that is "about", "blob", "data", "file",
+  // "filesystem", or a network scheme." [spec text]
+  return scheme == "about" || scheme == "blob" || scheme == "data" ||
+         scheme == "file" || scheme == "filesystem" || scheme == "ftp" ||
+         scheme == "http" || scheme == "https";
+}
+
 void SchemeRegistry::RegisterURLSchemeAsFirstPartyWhenTopLevel(
     const String& scheme) {
   DCHECK_EQ(scheme, scheme.LowerASCII());
diff --git a/third_party/blink/renderer/platform/weborigin/scheme_registry.h b/third_party/blink/renderer/platform/weborigin/scheme_registry.h
index 79b4b53..075436f 100644
--- a/third_party/blink/renderer/platform/weborigin/scheme_registry.h
+++ b/third_party/blink/renderer/platform/weborigin/scheme_registry.h
@@ -108,6 +108,9 @@
   static void RegisterURLSchemeAsSupportingFetchAPI(const String& scheme);
   static bool ShouldTreatURLSchemeAsSupportingFetchAPI(const String& scheme);
 
+  // https://fetch.spec.whatwg.org/#fetch-scheme
+  static bool IsFetchScheme(const String& scheme);
+
   // Schemes which override the first-/third-party checks on a Document.
   static void RegisterURLSchemeAsFirstPartyWhenTopLevel(const String& scheme);
   static void RemoveURLSchemeAsFirstPartyWhenTopLevel(const String& scheme);
diff --git a/third_party/blink/renderer/platform/wtf/stack_util.cc b/third_party/blink/renderer/platform/wtf/stack_util.cc
index 248f9396..312c8771 100644
--- a/third_party/blink/renderer/platform/wtf/stack_util.cc
+++ b/third_party/blink/renderer/platform/wtf/stack_util.cc
@@ -33,7 +33,7 @@
 #if defined(__GLIBC__) || defined(OS_ANDROID) || defined(OS_FREEBSD) || \
     defined(OS_FUCHSIA)
   // pthread_getattr_np() can fail if the thread is not invoked by
-  // pthread_create() (e.g., the main thread of webkit_unit_tests).
+  // pthread_create() (e.g., the main thread of blink_unittests).
   // If so, a conservative size estimate is returned.
 
   pthread_attr_t attr;
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 1cf8b78..787ed91 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3940,7 +3940,6 @@
 crbug.com/626703 external/wpt/presentation-api/controlling-ua/getAvailability.https.html [ Timeout ]
 crbug.com/626703 external/wpt/requestidlecallback/callback-timeout.html [ Timeout ]
 crbug.com/626703 external/wpt/requestidlecallback/callback-xhr-sync.html [ Timeout ]
-crbug.com/626703 external/wpt/screen-orientation/onchange-event.html [ Timeout ]
 crbug.com/626703 external/wpt/screen-orientation/onchange-event-subframe.html [ Timeout ]
 crbug.com/648295 external/wpt/service-workers/service-worker/update-bytecheck.https.html [ Timeout ]
 crbug.com/626703 virtual/outofblink-cors/external/wpt/service-workers/service-worker/update-bytecheck.https.html [ Timeout ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index 528b4537..af04a746 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -182908,6 +182908,11 @@
      {}
     ]
    ],
+   "screen-orientation/onchange-event-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "screen-orientation/orientation-reading-expected.txt": [
     [
      {}
@@ -226022,6 +226027,12 @@
      {}
     ]
    ],
+   "element-timing/observe-svg-image.html": [
+    [
+     "/element-timing/observe-svg-image.html",
+     {}
+    ]
+   ],
    "element-timing/progressively-loaded-image.html": [
     [
      "/element-timing/progressively-loaded-image.html",
@@ -277073,7 +277084,9 @@
    "screen-orientation/lock-basic.html": [
     [
      "/screen-orientation/lock-basic.html",
-     {}
+     {
+      "testdriver": true
+     }
     ]
    ],
    "screen-orientation/lock-sandboxed-iframe.html": [
@@ -310050,7 +310063,7 @@
    "support"
   ],
   "common/sleep.py": [
-   "28065eff5fc907dbfb69f4c69020d049c7daa35d",
+   "afc0a7c26e1c2aa63a54bcf39d74cf9c7adf6d75",
    "support"
   ],
   "common/slow.py": [
@@ -394294,11 +394307,11 @@
    "support"
   ],
   "domparsing/XMLSerializer-serializeToString-expected.txt": [
-   "e727b948d30dd8e7f65cf5a0c11dc0acd4c5e775",
+   "e7272dd7fa2ec543b2c4064230e30e3fd3a2fb26",
    "support"
   ],
   "domparsing/XMLSerializer-serializeToString.html": [
-   "e2e72e39eb5f849bbaf9302af6f3301a6cb36fa8",
+   "5ae5bbb82b86909365a071f984b834c2e97f66fd",
    "testharness"
   ],
   "domparsing/createContextualFragment.html": [
@@ -395193,6 +395206,10 @@
    "e56092c65bf4ab422875b54e25a5315d9f6da8c9",
    "testharness"
   ],
+  "element-timing/observe-svg-image.html": [
+   "327ab69668b20addd31df5e9ae2ce655f021d81b",
+   "testharness"
+  ],
   "element-timing/progressively-loaded-image.html": [
    "6fdff39d53848546e113b72ca17e38fd9b0dabc7",
    "testharness"
@@ -446250,19 +446267,23 @@
    "support"
   ],
   "screen-orientation/lock-basic.html": [
-   "5b30459bf7fab739c34dd0723e60e9ab46c5a9ea",
+   "c42aba783e4c0a593a3c71c14ea4ea792953a5dc",
    "testharness"
   ],
   "screen-orientation/lock-sandboxed-iframe.html": [
    "1041f91886a87083ed78227e15badb071395dfb4",
    "testharness"
   ],
+  "screen-orientation/onchange-event-expected.txt": [
+   "76af39c5c08a2a59d1699fe78e04253db0db86a3",
+   "support"
+  ],
   "screen-orientation/onchange-event-subframe.html": [
    "869a2294594aecc6e827501a3802d3cc42bde630",
    "testharness"
   ],
   "screen-orientation/onchange-event.html": [
-   "538fafc41ec317288b58b85cfa17c8ff3c705f08",
+   "401af096631a2cfdf44f3c920454eb1958925c3c",
    "testharness"
   ],
   "screen-orientation/orientation-reading-expected.txt": [
@@ -448498,7 +448519,7 @@
    "testharness"
   ],
   "service-workers/service-worker/postmessage-to-client-message-queue.https.html": [
-   "caa4f9445d3d8ad0ab92ba73713da17a33af185c",
+   "83e5f4540d1411e2a215ebe59d2037f369c48616",
    "testharness"
   ],
   "service-workers/service-worker/postmessage-to-client.https.html": [
diff --git a/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString-expected.txt b/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString-expected.txt
index e7272dd..fc008ec 100644
--- a/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString-expected.txt
@@ -2,6 +2,7 @@
 PASS check XMLSerializer.serializeToString method could parsing xmldoc to string
 PASS Check if the default namespace is correctly reset.
 PASS Check if there is no redundant empty namespace declaration.
+PASS Check if inconsistent xmlns="..." is dropped.
 PASS Check if an attribute with namespace and no prefix is serialized with the nearest-declared prefix
 FAIL Check if an attribute with namespace and no prefix is serialized with the nearest-declared prefix even if the prefix is assigned to another namespace. assert_equals: expected "<el1 xmlns:p=\"u1\" xmlns:q=\"u1\"><el2 xmlns:q=\"u2\" q:name=\"v\"/></el1>" but got "<el1 xmlns:p=\"u1\" xmlns:q=\"u1\"><el2 xmlns:q=\"u2\" p:name=\"v\"/></el1>"
 PASS Check if the prefix of an attribute is replaced with another existing prefix mapped to the same namespace URI.
diff --git a/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString.html b/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString.html
index 5ae5bbb..5f7e2bb0 100644
--- a/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString.html
+++ b/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString.html
@@ -9,6 +9,8 @@
  <body>
     <h1>domparsing_XMLSerializer_serializeToString</h1>
   <script>
+const XMLNS_URI = 'http://www.w3.org/2000/xmlns/';
+
 function createXmlDoc(){
   var input = '<?xml version="1.0" encoding="UTF-8"?><root><child1>value1</child1></root>';
   var parser = new DOMParser();
@@ -50,6 +52,26 @@
 }, 'Check if there is no redundant empty namespace declaration.');
 
 test(function() {
+  const root = parse('<root xmlns="uri1"/>');
+  const child = root.ownerDocument.createElement('child');
+  child.setAttributeNS(XMLNS_URI, 'xmlns', 'FAIL1');
+  root.appendChild(child);
+  const child2 = root.ownerDocument.createElementNS('uri2', 'child2');
+  child2.setAttributeNS(XMLNS_URI, 'xmlns', 'FAIL2');
+  root.appendChild(child2);
+  const child3 = root.ownerDocument.createElementNS('uri1', 'child3');
+  child3.setAttributeNS(XMLNS_URI, 'xmlns', 'FAIL3');
+  root.appendChild(child3);
+  const child4 = root.ownerDocument.createElementNS('uri4', 'child4');
+  child4.setAttributeNS(XMLNS_URI, 'xmlns', 'uri4');
+  root.appendChild(child4);
+  const child5 = root.ownerDocument.createElement('child5');
+  child5.setAttributeNS(XMLNS_URI, 'xmlns', '');
+  root.appendChild(child5);
+  assert_equals(serialize(root), '<root xmlns="uri1"><child xmlns=""/><child2 xmlns="uri2"/><child3/><child4 xmlns="uri4"/><child5 xmlns=""/></root>');
+}, 'Check if inconsistent xmlns="..." is dropped.');
+
+test(function() {
   let root = parse('<r xmlns:xx="uri"></r>');
   root.setAttributeNS('uri', 'name', 'v');
   assert_equals(serialize(root), '<r xmlns:xx="uri" xx:name="v"/>');
diff --git a/third_party/blink/web_tests/external/wpt/screen-orientation/lock-basic.html b/third_party/blink/web_tests/external/wpt/screen-orientation/lock-basic.html
index 5b30459..c42aba783 100644
--- a/third_party/blink/web_tests/external/wpt/screen-orientation/lock-basic.html
+++ b/third_party/blink/web_tests/external/wpt/screen-orientation/lock-basic.html
@@ -1,6 +1,8 @@
 <!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>
 test(() => {
   screen.orientation.unlock();
@@ -12,11 +14,18 @@
 }, "Test that screen.orientation.unlock() returns a void value");
 
 promise_test(async t => {
+  await test_driver.bless("request full screen", () => {
+    return document.documentElement.requestFullscreen();
+  });
   const value = await screen.orientation.lock('any');
   assert_equals(value, undefined);
+  return document.exitFullscreen();
 }, "Test that screen.orientation.lock returns a promise which will be fulfilled with a void value.");
 
 promise_test(async t => {
+  await test_driver.bless("request full screen", () => {
+    return document.documentElement.requestFullscreen();
+  });
   const orientations = [
     'any',
     'natural',
@@ -50,9 +59,13 @@
     }
   }
   screen.orientation.unlock();
+  return document.exitFullscreen();
 }, "Test that screen.orientation.lock returns a pending promise.");
 
 promise_test(async t => {
+  await test_driver.bless("request full screen", () => {
+    return document.documentElement.requestFullscreen();
+  });
   const preType = screen.orientation.type;
   const isPortrait = preType.includes("portrait");
   const newType = `${ isPortrait ? "landscape" : "portrait" }-primary`;
@@ -60,5 +73,6 @@
   assert_equals(screen.orientation.type, preType, "Must not change orientation until next spin of event loop");
   await p;
   assert_equals(screen.orientation.type, newType);
+  return document.exitFullscreen();
 }, "Test that screen.orientation.lock() is actually async");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/screen-orientation/onchange-event-expected.txt b/third_party/blink/web_tests/external/wpt/screen-orientation/onchange-event-expected.txt
new file mode 100644
index 0000000..76af39c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/screen-orientation/onchange-event-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Test that orientationchange event is not fired when the orientation does not change. promise_test: Unhandled rejection with value: object "ReferenceError: test_driver is not defined"
+FAIL Test that orientationchange event is fired when the orientation changes. promise_test: Unhandled rejection with value: object "ReferenceError: test_driver is not defined"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/screen-orientation/onchange-event.html b/third_party/blink/web_tests/external/wpt/screen-orientation/onchange-event.html
index 538fafc..401af09 100644
--- a/third_party/blink/web_tests/external/wpt/screen-orientation/onchange-event.html
+++ b/third_party/blink/web_tests/external/wpt/screen-orientation/onchange-event.html
@@ -3,13 +3,20 @@
 <script src="/resources/testharnessreport.js"></script>
 <script>
 promise_test(async t => {
+  await test_driver.bless("request full screen", () => {
+    return document.documentElement.requestFullscreen();
+  });
   const type = screen.orientation.type;
   screen.orientation.onchange = t.unreached_func("change event should not be fired");
   await screen.orientation.lock(type);
   assert_equals(screen.orientation.type, type);
+  return document.exitFullscreen();
 }, "Test that orientationchange event is not fired when the orientation does not change.");
 
 promise_test(async t => {
+  await test_driver.bless("request full screen", () => {
+    return document.documentElement.requestFullscreen();
+  });
   let orientations = [
     'portrait-primary',
     'portrait-secondary',
@@ -27,5 +34,6 @@
     assert_equals(screen.orientation.type, orientation);
   }
   screen.orientation.unlock();
+  return document.exitFullscreen();
 }, "Test that orientationchange event is fired when the orientation changes.");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
index 7c95394..bb95df8 100755
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
@@ -272,4 +272,7 @@
   -miRecordSize 100 \
   -ignoreErrors true
 
+# Signed Exchange with payload integrity error.
+echo 'garbage' | cat sxg/sxg-location.sxg - >sxg/sxg-merkle-integrity-error.sxg
+
 rm -fr $tmpdir
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-merkle-integrity-error.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-merkle-integrity-error.sxg
new file mode 100644
index 0000000..0c6dce0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-merkle-integrity-error.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-merkle-integrity-error.tentative.html b/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-merkle-integrity-error.tentative.html
new file mode 100644
index 0000000..5f923d2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-merkle-integrity-error.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>SignedHTTPExchange with payload integrity error</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="./resources/sxg-util.js"></script>
+<body>
+<script>
+promise_test(async (t) => {
+  try {
+    const sxgUrl = get_host_info().HTTPS_ORIGIN + '/signed-exchange/resources/sxg/sxg-merkle-integrity-error.sxg';
+    const message = await openSXGInIframeAndWaitForMessage(t, sxgUrl);
+    if (message.is_fallback) {
+        assert_unreached('Fallback redirect should not have happened');
+    } else {
+        assert_unreached('SXG should not have loaded');
+    }
+  } catch (e) {
+    assert_equals(e, 'timeout');
+  }
+}, "SignedHTTPExchange with payload integrity error");
+
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-prefetch-merkle-integrity-error.tentative.html b/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-prefetch-merkle-integrity-error.tentative.html
new file mode 100644
index 0000000..2086c38
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-prefetch-merkle-integrity-error.tentative.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Prefetching SignedHTTPExchange with payload integrity error should fail</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="./resources/sxg-util.js"></script>
+<body>
+<script>
+promise_test(async (t) => {
+  const sxgUrl = get_host_info().HTTPS_ORIGIN + '/signed-exchange/resources/sxg/sxg-merkle-integrity-error.sxg';
+  await new Promise(resolve => {
+    const link = document.createElement('link');
+    link.rel = 'prefetch';
+    link.href = sxgUrl;
+    link.addEventListener('error', t.step_func(() => {
+        resolve();
+    }));
+    link.addEventListener('load', t.step_func(() => {
+        assert_unreached('Prefetch should fail');
+    }));
+    document.body.appendChild(link);
+  });
+}, "Prefetching SignedHTTPExchange with payload integrity error should fail");
+
+</script>
+</body>
diff --git a/third_party/blink/web_tests/http/tests/devtools/network/network-memory-cached-resource-expected.txt b/third_party/blink/web_tests/http/tests/devtools/network/network-memory-cached-resource-expected.txt
index 11c32d1..bd8e7e8 100644
--- a/third_party/blink/web_tests/http/tests/devtools/network/network-memory-cached-resource-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/network/network-memory-cached-resource-expected.txt
@@ -1,5 +1,6 @@
 Tests that memory-cached resources are correctly reported.
 
+An uncached resource is found.
 Page reloaded.
-Memory-cached resource found.
+A cached resource is found.
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/network/network-memory-cached-resource.js b/third_party/blink/web_tests/http/tests/devtools/network/network-memory-cached-resource.js
index 981a8965..2a12c178 100644
--- a/third_party/blink/web_tests/http/tests/devtools/network/network-memory-cached-resource.js
+++ b/third_party/blink/web_tests/http/tests/devtools/network/network-memory-cached-resource.js
@@ -8,36 +8,30 @@
   await TestRunner.showPanel('network');
   await TestRunner.navigatePromise(`resources/memory-cached-resource.html`);
 
-  var finished = false;
-  TestRunner.NetworkAgent.setCacheDisabled(true).then(step1);
-
-  function findResource(url, status, cached) {
-    return NetworkTestRunner.networkRequests().find(
-        request => url.test(request.url()) && (status === request.statusCode) && (cached === request.cached()));
+  function waitOnResource(url, status, cached) {
+    return new Promise(resolve => {
+      const eventName = SDK.NetworkManager.Events.RequestFinished;
+      function onRequestFinished(event) {
+        const request = event.data;
+        if (url.test(request.url()) && status === request.statusCode && cached === request.cached()) {
+          TestRunner.networkManager.removeEventListener(eventName, onRequestFinished);
+          resolve(request);
+        }
+      }
+      TestRunner.networkManager.addEventListener(eventName, onRequestFinished);
+    });
   }
 
-  function step1() {
-    TestRunner.networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, onRequest);
-    TestRunner.reloadPage(step2);
-  }
+  await TestRunner.NetworkAgent.setCacheDisabled(true);
+  await TestRunner.reloadPage();
+  await waitOnResource(/abe\.png/, 200, false);
+  TestRunner.addResult('An uncached resource is found.');
 
-  function step2() {
-    TestRunner.addIframe('memory-cached-resource.html');
-  }
+  await TestRunner.NetworkAgent.setCacheDisabled(false);
+  const cached = waitOnResource(/abe\.png/, 200, true);
+  await TestRunner.addIframe('memory-cached-resource.html');
+  await cached;
+  TestRunner.addResult('A cached resource is found.');
 
-  function onRequest() {
-    if (!finished && findResource(/abe\.png/, 200, false) && findResource(/abe\.png/, 200, true)) {
-      finished = true;
-      TestRunner.addResult('Memory-cached resource found.');
-      step3();
-    }
-  }
-
-  function step3() {
-    TestRunner.NetworkAgent.setCacheDisabled(false).then(step4);
-  }
-
-  function step4() {
-    TestRunner.completeTest();
-  }
+  TestRunner.completeTest();
 })();
diff --git a/third_party/blink/web_tests/http/tests/devtools/network/resources/memory-cached-resource.html b/third_party/blink/web_tests/http/tests/devtools/network/resources/memory-cached-resource.html
index 39c4945..690318b 100644
--- a/third_party/blink/web_tests/http/tests/devtools/network/resources/memory-cached-resource.html
+++ b/third_party/blink/web_tests/http/tests/devtools/network/resources/memory-cached-resource.html
@@ -1,3 +1,9 @@
 <html>
-<img src="abe.png">
-</html>
\ No newline at end of file
+<body></body>
+<script>
+// We have this as a script to suppress preloading.
+const elem = document.createElement('img');
+elem.src = 'abe.png';
+document.body.appendChild(elem);
+</script>
+</html>
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index 53dc7d7..43af897 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -309,9 +309,9 @@
   SCROLL_FORWARD: 'scrollForward',
   SCROLL_LEFT: 'scrollLeft',
   SCROLL_RIGHT: 'scrollRight',
+  SCROLL_UP: 'scrollUp',
   SCROLL_TO_MAKE_VISIBLE: 'scrollToMakeVisible',
   SCROLL_TO_POINT: 'scrollToPoint',
-  SCROLL_UP: 'scrollUp',
   SET_ACCESSIBILITY_FOCUS: 'setAccessibilityFocus',
   SET_SCROLL_OFFSET: 'setScrollOffset',
   SET_SELECTION: 'setSelection',
@@ -1182,7 +1182,7 @@
 chrome.automation.AutomationNode.prototype.imageDataUrl;
 
 /**
- * The language code for this subtree.
+ * The author-provided language code for this subtree.
  * @type {(string|undefined)}
  * @see https://developer.chrome.com/extensions/automation#type-language
  */
@@ -1191,6 +1191,7 @@
 /**
  * The detected language code for this subtree.
  * @type {(string|undefined)}
+ * @see https://developer.chrome.com/extensions/automation#type-detectedLanguage
  */
 chrome.automation.AutomationNode.prototype.detectedLanguage;
 
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index 5279e05..df9d8e4 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -635,7 +635,7 @@
  * backend. If resolving entry fails, the entry will be just ignored and the
  * corresponding entry does not appear in the result.
  * @param {!Array<!Entry>} entries
- * @param {function((!Array<!Entry>|undefined))} callback Completion callback
+ * @param {function(!Array<!Entry>):void} callback Completion callback
  *     with resolved entries.
  */
 chrome.fileManagerPrivate.resolveIsolatedEntries = function(entries,
@@ -645,7 +645,7 @@
  * Mount a resource or a file. |source| Mount point source. For compressed
  * files it is relative file path     within external file system |callback|
  * @param {string} source
- * @param {function((string|undefined))} callback Callback with source path of
+ * @param {function(string):void} callback callback Callback with source path of
  *     the mount.
  */
 chrome.fileManagerPrivate.addMount = function(source, callback) {};
@@ -785,7 +785,8 @@
 
 /**
  * Retrieves the state of the current drive connection. |callback|
- * @param {function((!chrome.fileManagerPrivate.DriveConnectionState|undefined))} callback
+ * @param {function(!chrome.fileManagerPrivate.DriveConnectionState):void}
+ *     callback
  */
 chrome.fileManagerPrivate.getDriveConnectionState = function(callback) {};
 
diff --git a/third_party/inspector_protocol/README.chromium b/third_party/inspector_protocol/README.chromium
index fb719e9..10cf881 100644
--- a/third_party/inspector_protocol/README.chromium
+++ b/third_party/inspector_protocol/README.chromium
@@ -2,7 +2,7 @@
 Short Name: inspector_protocol
 URL: https://chromium.googlesource.com/deps/inspector_protocol/
 Version: 0
-Revision: I69fa904098f44b7d1219b6a2772231df7651ac35
+Revision: ec358ccfd63a2a657c147329c7793d217e278a58
 License: BSD
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/inspector_protocol/lib/Values_cpp.template b/third_party/inspector_protocol/lib/Values_cpp.template
index 55455076..6b4a7856 100644
--- a/third_party/inspector_protocol/lib/Values_cpp.template
+++ b/third_party/inspector_protocol/lib/Values_cpp.template
@@ -60,10 +60,156 @@
     }
 }
 
+// When parsing CBOR, we limit recursion depth for objects and arrays
+// to this constant.
+static constexpr int kStackLimitValues = 1000;
+
+// Below are three parsing routines for CBOR, which cover enough
+// to roundtrip JSON messages.
+std::unique_ptr<DictionaryValue> parseMap(int32_t stack_depth, CBORTokenizer* tokenizer);
+std::unique_ptr<ListValue> parseArray(int32_t stack_depth, CBORTokenizer* tokenizer);
+std::unique_ptr<Value> parseValue(int32_t stack_depth, CBORTokenizer* tokenizer);
+
+// |bytes| must start with the indefinite length array byte, so basically,
+// ParseArray may only be called after an indefinite length array has been
+// detected.
+std::unique_ptr<ListValue> parseArray(int32_t stack_depth, CBORTokenizer* tokenizer) {
+  DCHECK(tokenizer->TokenTag() == CBORTokenTag::ARRAY_START);
+  tokenizer->Next();
+  auto list = ListValue::create();
+  while (tokenizer->TokenTag() != CBORTokenTag::STOP) {
+    // Error::CBOR_UNEXPECTED_EOF_IN_ARRAY
+    if (tokenizer->TokenTag() == CBORTokenTag::DONE) return nullptr;
+    if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
+    // Parse value.
+    auto value = parseValue(stack_depth, tokenizer);
+    if (!value) return nullptr;
+    list->pushValue(std::move(value));
+  }
+  tokenizer->Next();
+  return list;
+}
+
+std::unique_ptr<Value> parseValue(
+    int32_t stack_depth, CBORTokenizer* tokenizer) {
+  // Error::CBOR_STACK_LIMIT_EXCEEDED
+  if (stack_depth > kStackLimitValues) return nullptr;
+  // Skip past the envelope to get to what's inside.
+  if (tokenizer->TokenTag() == CBORTokenTag::ENVELOPE)
+    tokenizer->EnterEnvelope();
+  switch (tokenizer->TokenTag()) {
+    case CBORTokenTag::ERROR_VALUE:
+      return nullptr;
+    case CBORTokenTag::DONE:
+      // Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE
+      return nullptr;
+    case CBORTokenTag::TRUE_VALUE: {
+      std::unique_ptr<Value> value = FundamentalValue::create(true);
+      tokenizer->Next();
+      return value;
+    }
+    case CBORTokenTag::FALSE_VALUE: {
+      std::unique_ptr<Value> value = FundamentalValue::create(false);
+      tokenizer->Next();
+      return value;
+    }
+    case CBORTokenTag::NULL_VALUE: {
+      std::unique_ptr<Value> value = FundamentalValue::null();
+      tokenizer->Next();
+      return value;
+    }
+    case CBORTokenTag::INT32: {
+      std::unique_ptr<Value> value = FundamentalValue::create(tokenizer->GetInt32());
+      tokenizer->Next();
+      return value;
+    }
+    case CBORTokenTag::DOUBLE: {
+      std::unique_ptr<Value> value = FundamentalValue::create(tokenizer->GetDouble());
+      tokenizer->Next();
+      return value;
+    }
+    case CBORTokenTag::STRING8: {
+      span<uint8_t> str = tokenizer->GetString8();
+      std::unique_ptr<Value> value = StringValue::create(StringUtil::fromUTF8(str.data(), str.size()));
+      tokenizer->Next();
+      return value;
+    }
+    case CBORTokenTag::STRING16:
+      // NOT SUPPORTED YET.
+      return nullptr;
+    case CBORTokenTag::BINARY: {
+      span<uint8_t> payload = tokenizer->GetBinary();
+      return BinaryValue::create(Binary::fromSpan(payload.data(), payload.size()));
+    }
+    case CBORTokenTag::MAP_START:
+      return parseMap(stack_depth + 1, tokenizer);
+    case CBORTokenTag::ARRAY_START:
+      return parseArray(stack_depth + 1, tokenizer);
+    default:
+      // Error::CBOR_UNSUPPORTED_VALUE
+      return nullptr;
+  }
+}
+
+// |bytes| must start with the indefinite length array byte, so basically,
+// ParseArray may only be called after an indefinite length array has been
+// detected.
+std::unique_ptr<DictionaryValue> parseMap(
+    int32_t stack_depth, CBORTokenizer* tokenizer) {
+  auto dict = DictionaryValue::create();
+  tokenizer->Next();
+  while (tokenizer->TokenTag() != CBORTokenTag::STOP) {
+    if (tokenizer->TokenTag() == CBORTokenTag::DONE) {
+      // Error::CBOR_UNEXPECTED_EOF_IN_MAP
+      return nullptr;
+    }
+    if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
+    // Parse key.
+    String key;
+    if (tokenizer->TokenTag() == CBORTokenTag::STRING8) {
+      span<uint8_t> key_span = tokenizer->GetString8();
+      key = StringUtil::fromUTF8(key_span.data(), key_span.size());
+    } else if (tokenizer->TokenTag() == CBORTokenTag::STRING16) {
+      return nullptr;  // STRING16 not supported yet.
+    } else {
+      // Error::CBOR_INVALID_MAP_KEY
+      return nullptr;
+    }
+    // Parse value.
+    auto value = parseValue(stack_depth, tokenizer);
+    if (!value) return nullptr;
+  }
+  tokenizer->Next();
+  return dict;
+}
+
 } // anonymous namespace
 
 // static
 std::unique_ptr<Value> Value::parseBinary(const uint8_t* data, size_t size) {
+  span<uint8_t> bytes(data, size);
+
+  // Error::CBOR_NO_INPUT
+  if (bytes.empty()) return nullptr;
+
+  // Error::CBOR_INVALID_START_BYTE
+  // TODO(johannes): EncodeInitialByteForEnvelope() method.
+  if (bytes[0] != 0xd8) return nullptr;
+
+  CBORTokenizer tokenizer(bytes);
+  if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
+
+  // We checked for the envelope start byte above, so the tokenizer
+  // must agree here, since it's not an error.
+  DCHECK(tokenizer.TokenTag() == CBORTokenTag::ENVELOPE);
+  tokenizer.EnterEnvelope();
+  // Error::MAP_START_EXPECTED
+  if (tokenizer.TokenTag() != CBORTokenTag::MAP_START) return nullptr;
+  std::unique_ptr<Value> result = parseMap(/*stack_depth=*/1, &tokenizer);
+  if (!result) return nullptr;
+  if (tokenizer.TokenTag() == CBORTokenTag::DONE) return result;
+  if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
+  // Error::CBOR_TRAILING_JUNK
   return nullptr;
 }
 
@@ -87,12 +233,22 @@
     return false;
 }
 
+bool Value::asBinary(Binary*) const
+{
+    return false;
+}
+
 void Value::writeJSON(StringBuilder* output) const
 {
     DCHECK(m_type == TypeNull);
     StringUtil::builderAppend(*output, nullValueString, 4);
 }
 
+void Value::writeBinary(std::vector<uint8_t>* bytes) const {
+    DCHECK(m_type == TypeNull);
+    bytes->push_back(EncodeNull());
+}
+
 std::unique_ptr<Value> Value::clone() const
 {
     return Value::null();
@@ -111,7 +267,9 @@
 }
 
 std::vector<uint8_t> Value::serializeToBinary() {
-    return std::vector<uint8_t>();
+    std::vector<uint8_t> bytes;
+    writeBinary(&bytes);
+    return bytes;
 }
 
 bool FundamentalValue::asBoolean(bool* output) const
@@ -162,6 +320,22 @@
     }
 }
 
+void FundamentalValue::writeBinary(std::vector<uint8_t>* bytes) const {
+    switch (type()) {
+    case TypeDouble:
+        EncodeDouble(m_doubleValue, bytes);
+        return;
+    case TypeInteger:
+        EncodeInt32(m_integerValue, bytes);
+        return;
+    case TypeBoolean:
+        bytes->push_back(m_boolValue ? EncodeTrue() : EncodeFalse());
+        return;
+    default:
+        DCHECK(false);
+    }
+}
+
 std::unique_ptr<Value> FundamentalValue::clone() const
 {
     switch (type()) {
@@ -186,17 +360,50 @@
     StringUtil::builderAppendQuotedString(*output, m_stringValue);
 }
 
+void StringValue::writeBinary(std::vector<uint8_t>* bytes) const {
+    StringUTF8Adapter utf8(m_stringValue);
+    EncodeString8(span<uint8_t>(reinterpret_cast<const uint8_t*>(utf8.Data()),
+                  utf8.length()), bytes);
+}
+
 std::unique_ptr<Value> StringValue::clone() const
 {
     return StringValue::create(m_stringValue);
 }
 
+bool BinaryValue::asBinary(Binary* output) const
+{
+    *output = m_binaryValue;
+    return true;
+}
+
+void BinaryValue::writeJSON(StringBuilder* output) const
+{
+    DCHECK(type() == TypeBinary);
+    StringUtil::builderAppendQuotedString(*output, m_binaryValue.toBase64());
+}
+
+void BinaryValue::writeBinary(std::vector<uint8_t>* bytes) const {
+    EncodeBinary(span<uint8_t>(m_binaryValue.data(), m_binaryValue.size()), bytes);
+}
+
+std::unique_ptr<Value> BinaryValue::clone() const
+{
+    return BinaryValue::create(m_binaryValue);
+}
+
 void SerializedValue::writeJSON(StringBuilder* output) const
 {
     DCHECK(type() == TypeSerialized);
     StringUtil::builderAppend(*output, m_serializedJSON);
 }
 
+void SerializedValue::writeBinary(std::vector<uint8_t>* output) const
+{
+    DCHECK(type() == TypeSerialized);
+    output->insert(output->end(), m_serializedBinary.begin(), m_serializedBinary.end());
+}
+
 std::unique_ptr<Value> SerializedValue::clone() const
 {
     return std::unique_ptr<SerializedValue>(new SerializedValue(m_serializedJSON, m_serializedBinary));
@@ -339,6 +546,23 @@
     StringUtil::builderAppend(*output, '}');
 }
 
+void DictionaryValue::writeBinary(std::vector<uint8_t>* bytes) const {
+    EnvelopeEncoder encoder;
+    encoder.EncodeStart(bytes);
+    bytes->push_back(EncodeIndefiniteLengthMapStart());
+    for (size_t i = 0; i < m_order.size(); ++i) {
+        const String& key = m_order[i];
+        Dictionary::const_iterator value = m_data.find(key);
+        DCHECK(value != m_data.cend() && value->second);
+        StringUTF8Adapter utf8(key);
+        EncodeString8(span<uint8_t>(reinterpret_cast<const uint8_t*>(utf8.Data()),
+	                            utf8.length()), bytes);
+        value->second->writeBinary(bytes);
+    }
+    bytes->push_back(EncodeStop());
+    encoder.EncodeStop(bytes);
+}
+
 std::unique_ptr<Value> DictionaryValue::clone() const
 {
     std::unique_ptr<DictionaryValue> result = DictionaryValue::create();
@@ -373,6 +597,17 @@
     StringUtil::builderAppend(*output, ']');
 }
 
+void ListValue::writeBinary(std::vector<uint8_t>* bytes) const {
+    EnvelopeEncoder encoder;
+    encoder.EncodeStart(bytes);
+    bytes->push_back(EncodeIndefiniteLengthArrayStart());
+    for (size_t i = 0; i < m_data.size(); ++i) {
+        m_data[i]->writeBinary(bytes);
+    }
+    bytes->push_back(EncodeStop());
+    encoder.EncodeStop(bytes);
+}
+
 std::unique_ptr<Value> ListValue::clone() const
 {
     std::unique_ptr<ListValue> result = ListValue::create();
diff --git a/third_party/inspector_protocol/lib/Values_h.template b/third_party/inspector_protocol/lib/Values_h.template
index 320ea40..87ec007 100644
--- a/third_party/inspector_protocol/lib/Values_h.template
+++ b/third_party/inspector_protocol/lib/Values_h.template
@@ -36,6 +36,7 @@
         TypeInteger,
         TypeDouble,
         TypeString,
+        TypeBinary,
         TypeObject,
         TypeArray,
         TypeSerialized
@@ -49,14 +50,15 @@
     virtual bool asDouble(double* output) const;
     virtual bool asInteger(int* output) const;
     virtual bool asString(String* output) const;
+    virtual bool asBinary(Binary* output) const;
 
     virtual void writeJSON(StringBuilder* output) const;
+    virtual void writeBinary(std::vector<uint8_t>* bytes) const;
     virtual std::unique_ptr<Value> clone() const;
     String toJSONString() const;
     String serializeToJSON() override;
     std::vector<uint8_t> serializeToBinary() override;
 
-
 protected:
     Value() : m_type(TypeNull) { }
     explicit Value(ValueType type) : m_type(type) { }
@@ -89,6 +91,7 @@
     bool asDouble(double* output) const override;
     bool asInteger(int* output) const override;
     void writeJSON(StringBuilder* output) const override;
+    void writeBinary(std::vector<uint8_t>* bytes) const override;
     std::unique_ptr<Value> clone() const override;
 
 private:
@@ -117,6 +120,7 @@
 
     bool asString(String* output) const override;
     void writeJSON(StringBuilder* output) const override;
+    void writeBinary(std::vector<uint8_t>* bytes) const override;
     std::unique_ptr<Value> clone() const override;
 
 private:
@@ -126,6 +130,24 @@
     String m_stringValue;
 };
 
+class {{config.lib.export_macro}} BinaryValue : public Value {
+public:
+    static std::unique_ptr<BinaryValue> create(const Binary& value)
+    {
+        return std::unique_ptr<BinaryValue>(new BinaryValue(value));
+    }
+
+    bool asBinary(Binary* output) const override;
+    void writeJSON(StringBuilder* output) const override;
+    void writeBinary(std::vector<uint8_t>* bytes) const override;
+    std::unique_ptr<Value> clone() const override;
+
+private:
+    explicit BinaryValue(const Binary& value) : Value(TypeBinary), m_binaryValue(value) { }
+
+    Binary m_binaryValue;
+};
+
 class {{config.lib.export_macro}} SerializedValue : public Value {
 public:
     static std::unique_ptr<SerializedValue> fromJSON(const String& value)
@@ -139,6 +161,7 @@
     }
 
     void writeJSON(StringBuilder* output) const override;
+    void writeBinary(std::vector<uint8_t>* bytes) const override;
     std::unique_ptr<Value> clone() const override;
 
 private:
@@ -171,6 +194,7 @@
     }
 
     void writeJSON(StringBuilder* output) const override;
+    void writeBinary(std::vector<uint8_t>* bytes) const override;
     std::unique_ptr<Value> clone() const override;
 
     size_t size() const { return m_data.size(); }
@@ -239,6 +263,7 @@
     ~ListValue() override;
 
     void writeJSON(StringBuilder* output) const override;
+    void writeBinary(std::vector<uint8_t>* bytes) const override;
     std::unique_ptr<Value> clone() const override;
 
     void pushValue(std::unique_ptr<Value>);
diff --git a/third_party/inspector_protocol/lib/base_string_adapter_cc.template b/third_party/inspector_protocol/lib/base_string_adapter_cc.template
index 1576cd7..852822b 100644
--- a/third_party/inspector_protocol/lib/base_string_adapter_cc.template
+++ b/third_party/inspector_protocol/lib/base_string_adapter_cc.template
@@ -223,6 +223,12 @@
   return Binary(base::RefCountedString::TakeString(&data));
 }
 
+// static
+Binary Binary::fromSpan(const uint8_t* data, size_t size) {
+  return Binary(scoped_refptr<base::RefCountedBytes>(
+      new base::RefCountedBytes(data, size)));
+}
+
 {% for namespace in config.protocol.namespace %}
 } // namespace {{namespace}}
 {% endfor %}
diff --git a/third_party/inspector_protocol/lib/base_string_adapter_h.template b/third_party/inspector_protocol/lib/base_string_adapter_h.template
index f20f1f5..76c05f652 100644
--- a/third_party/inspector_protocol/lib/base_string_adapter_h.template
+++ b/third_party/inspector_protocol/lib/base_string_adapter_h.template
@@ -124,6 +124,7 @@
   static Binary fromRefCounted(scoped_refptr<base::RefCountedMemory> memory);
   static Binary fromVector(std::vector<uint8_t> data);
   static Binary fromString(std::string data);
+  static Binary fromSpan(const uint8_t* data, size_t size);
 
  private:
   explicit Binary(scoped_refptr<base::RefCountedMemory> bytes);
diff --git a/third_party/tcmalloc/chromium/src/gperftools/malloc_extension.h b/third_party/tcmalloc/chromium/src/gperftools/malloc_extension.h
index 077aef8b..d7759340 100644
--- a/third_party/tcmalloc/chromium/src/gperftools/malloc_extension.h
+++ b/third_party/tcmalloc/chromium/src/gperftools/malloc_extension.h
@@ -53,6 +53,19 @@
 #include <string>
 #include <vector>
 
+#ifdef __has_attribute
+#define MALLOC_HAVE_ATTRIBUTE(x) __has_attribute(x)
+#else
+#define MALLOC_HAVE_ATTRIBUTE(x) 0
+#endif
+
+#if MALLOC_HAVE_ATTRIBUTE(unused)
+#undef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+#else
+#define ATTRIBUTE_UNUSED
+#endif
+
 // Annoying stuff for windows -- makes sure clients can import these functions
 #ifndef PERFTOOLS_DLL_DECL
 # ifdef _WIN32
@@ -334,7 +347,10 @@
   virtual Ownership GetOwnership(const void* p);
 
   // The current malloc implementation.  Always non-NULL.
-  static MallocExtension* instance();
+  static MallocExtension* instance() {
+    InitModuleOnce();
+    return current_instance_.load(std::memory_order_acquire);
+  }
 
   // Change the malloc implementation.  Typically called by the
   // malloc implementation during initialization.
@@ -412,6 +428,18 @@
   // have an empty cache but will not need to pay to reconstruct the
   // cache data structures.
   virtual void MarkThreadTemporarilyIdle();
+
+ private:
+  static MallocExtension* InitModule();
+
+  static void InitModuleOnce() {
+    // Pointer stored here so heap leak checker will consider the default
+    // instance reachable, even if current_instance_ is later overridden by
+    // MallocExtension::Register().
+    ATTRIBUTE_UNUSED static MallocExtension* default_instance = InitModule();
+  }
+
+  static std::atomic<MallocExtension*> current_instance_;
 };
 
 namespace base {
diff --git a/third_party/tcmalloc/chromium/src/malloc_extension.cc b/third_party/tcmalloc/chromium/src/malloc_extension.cc
index 6e69552..90b2a78 100644
--- a/third_party/tcmalloc/chromium/src/malloc_extension.cc
+++ b/third_party/tcmalloc/chromium/src/malloc_extension.cc
@@ -42,6 +42,7 @@
 #else
 #include <sys/types.h>
 #endif
+#include <atomic>
 #include <string>
 #include "base/dynamic_annotations.h"
 #include "base/sysinfo.h"    // for FillProcSelfMaps
@@ -203,34 +204,27 @@
 
 // The current malloc extension object.
 
-static MallocExtension* current_instance;
+std::atomic<MallocExtension*> MallocExtension::current_instance_;
 
-static void InitModule() {
-  if (current_instance != NULL) {
-    return;
-  }
-  current_instance = new MallocExtension;
+// static
+MallocExtension* MallocExtension::InitModule() {
+  MallocExtension* instance = new MallocExtension;
 #ifndef NO_HEAP_CHECK
-  HeapLeakChecker::IgnoreObject(current_instance);
+  HeapLeakChecker::IgnoreObject(instance);
 #endif
-}
-
-REGISTER_MODULE_INITIALIZER(malloc_extension_init, InitModule())
-
-MallocExtension* MallocExtension::instance() {
-  InitModule();
-  return current_instance;
+  current_instance_.store(instance, std::memory_order_release);
+  return instance;
 }
 
 void MallocExtension::Register(MallocExtension* implementation) {
-  InitModule();
+  InitModuleOnce();
   // When running under valgrind, our custom malloc is replaced with
   // valgrind's one and malloc extensions will not work.  (Note:
   // callers should be responsible for checking that they are the
   // malloc that is really being run, before calling Register.  This
   // is just here as an extra sanity check.)
   if (!RunningOnValgrind()) {
-    current_instance = implementation;
+    current_instance_.store(implementation, std::memory_order_release);
   }
 }
 
diff --git a/tools/binary_size/diagnose_bloat.py b/tools/binary_size/diagnose_bloat.py
index 427e560..24b8ba18 100755
--- a/tools/binary_size/diagnose_bloat.py
+++ b/tools/binary_size/diagnose_bloat.py
@@ -178,8 +178,7 @@
     """Generates diff lines for the specified sections (defaults to all)."""
     section_lines = collections.defaultdict(list)
     for section_name, section_results in self._diff.iteritems():
-      section_no_target = re.sub(r'^.*_', '', section_name)
-      if not include_sections or section_no_target in include_sections:
+      if not include_sections or section_name in include_sections:
         subsection_lines = []
         section_sum = 0
         units = ''
@@ -189,14 +188,14 @@
             continue
           section_sum += value
           subsection_lines.append('{:>+14,} {} {}'.format(value, units, name))
-        section_header = section_no_target
-        if section_no_target in ResourceSizesDiff._AGGREGATE_SECTIONS:
+        section_header = section_name
+        if section_name in ResourceSizesDiff._AGGREGATE_SECTIONS:
           section_header += ' ({:+,} {})'.format(section_sum, units)
         section_header += ':'
         # Omit sections with empty subsections.
         if subsection_lines:
-          section_lines[section_no_target].append(section_header)
-          section_lines[section_no_target].extend(subsection_lines)
+          section_lines[section_name].append(section_header)
+          section_lines[section_name].extend(subsection_lines)
     if not section_lines:
       return ['Empty ' + self.name]
     ret = []
@@ -208,7 +207,13 @@
     chartjson_file = os.path.join(archive_dir, self._filename)
     with open(chartjson_file) as f:
       chartjson = json.load(f)
-    return chartjson['charts']
+    charts = chartjson['charts']
+    # Older versions of resource_sizes.py prefixed the apk onto section names.
+    ret = {}
+    for section, section_dict in charts.iteritems():
+      section_no_target = re.sub(r'^.*_', '', section)
+      ret[section_no_target] = section_dict
+    return ret
 
 
 class _BuildHelper(object):
diff --git a/tools/binary_size/generate_milestone_reports.py b/tools/binary_size/generate_milestone_reports.py
new file mode 100755
index 0000000..e9215e2
--- /dev/null
+++ b/tools/binary_size/generate_milestone_reports.py
@@ -0,0 +1,288 @@
+#!/usr/bin/env python
+# 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.
+"""Generate report files to view and/or compare (diff) milestones.
+
+Size files are located in a Google Cloud Storage bucket for various Chrome
+versions. This script generates various HTML report files to view a single
+milesone, or to compare two milestones with the same CPU and APK.
+
+Desired CPUs, APKs, and milestone versions are set in constants below. If
+specified by the --skip-existing flag, the script checks what HTML report files
+have already been uploaded to the GCS bucket, then works on generating the
+remaining desired files.
+
+Size files are fetched by streaming them from the source bucket, then the
+html_report module handles creating a report file to diff two size files.
+Reports are saved to a local directory, and once all reports are created they
+can be uploaded to the destination bucket.
+
+Reports can be uploaded automatically with the --sync flag. Otherwise, they can
+be uploaded at a later point.
+"""
+
+import argparse
+import collections
+import contextlib
+import errno
+import itertools
+import json
+import logging
+import multiprocessing
+import os
+import re
+import shutil
+import sys
+import subprocess
+import tempfile
+
+_PUSH_URL = 'gs://chrome-supersize/milestones/'
+
+_DESIRED_CPUS = ['arm', 'arm_64']
+_DESIRED_APKS = ['Monochrome.apk', 'ChromeModern.apk', 'AndroidWebview.apk']
+# Versions are manually gathered from
+# https://omahaproxy.appspot.com/history?os=android&channel=stable
+_DESIRED_VERSIONS = [
+    '60.0.3112.116',
+    '61.0.3163.98',
+    '62.0.3202.84',
+    '63.0.3239.111',
+    '64.0.3282.137',
+    '65.0.3325.85',
+    '66.0.3359.158',
+    '67.0.3396.87',
+    '68.0.3440.85',
+    '69.0.3497.91',
+    '70.0.3538.64',
+    '71.0.3578.83',  # Beta
+    '72.0.3626.7',  # Beta
+]
+
+
+def _GetDesiredVersions(apk):
+  if apk != 'AndroidWebview.apk':
+    return _DESIRED_VERSIONS
+  # Webview .size files do not exist before M71.
+  return [v for v in _DESIRED_VERSIONS if int(v.split('.')[0]) >= 71]
+
+
+def _RequestedReports():
+  cpu_and_apk_combos = list(itertools.product(_DESIRED_CPUS, _DESIRED_APKS))
+  for cpu, apk in cpu_and_apk_combos:
+    apk_versions = _GetDesiredVersions(apk)
+    for after_version in apk_versions:
+      yield Report(cpu, apk, None, after_version)
+    for i, before_version in enumerate(apk_versions):
+      for after_version in apk_versions[i + 1:]:
+        yield Report(cpu, apk, before_version, after_version)
+
+
+def _TemplateToRegex(template):
+  # Transform '{cpu}/{apk}/... -> (?P<cpu>[^/]+)/(?P<apk>[^/]+)/...
+  pattern = re.sub(r'{(.*?)}', r'(?P<\1>[^/]+)', template)
+  return re.compile(pattern)
+
+
+class Report(
+    collections.namedtuple('Report',
+                           ['cpu', 'apk', 'before_version', 'after_version'])):
+
+  _NDJSON_TEMPLATE_VIEW = '{cpu}/{apk}/report_{after_version}.ndjson'
+  _NDJSON_TEMPLATE_COMPARE = (
+      '{cpu}/{apk}/report_{before_version}_{after_version}.ndjson')
+  _PUSH_URL_REGEX_VIEW = _TemplateToRegex(_PUSH_URL + _NDJSON_TEMPLATE_VIEW)
+  _PUSH_URL_REGEX_COMPARE = _TemplateToRegex(_PUSH_URL +
+                                             _NDJSON_TEMPLATE_COMPARE)
+  _SIZE_URL_TEMPLATE = '{version}/{cpu}/{apk}.size'
+
+  @classmethod
+  def FromUrl(cls, url):
+    # Perform this match first since it's more restrictive.
+    match = cls._PUSH_URL_REGEX_COMPARE.match(url)
+    if match:
+      return cls(**match.groupdict())
+    match = cls._PUSH_URL_REGEX_VIEW.match(url)
+    if match:
+      return cls(before_version=None, **match.groupdict())
+    return None
+
+  @property
+  def before_size_file_subpath(self):
+    if self.before_version:
+      return self._SIZE_URL_TEMPLATE.format(
+          version=self.before_version, **self._asdict())
+    return None
+
+  @property
+  def after_size_file_subpath(self):
+    return self._SIZE_URL_TEMPLATE.format(
+        version=self.after_version, **self._asdict())
+
+  @property
+  def ndjson_subpath(self):
+    if self.before_version:
+      return self._NDJSON_TEMPLATE_COMPARE.format(**self._asdict())
+    return self._NDJSON_TEMPLATE_VIEW.format(**self._asdict())
+
+
+def _MakeDirectory(path):
+  # Function is safe even from racing fork()ed processes.
+  try:
+    os.makedirs(path)
+  except OSError as e:
+    if e.errno != errno.EEXIST:
+      raise
+
+
+def _Shard(func, arg_tuples):
+  pool = multiprocessing.Pool()
+  try:
+    for x in pool.imap_unordered(func, arg_tuples):
+      yield x
+  finally:
+    pool.close()
+
+
+def _DownloadOneSizeFile(arg_tuples):
+  subpath, temp_dir, base_url = arg_tuples
+  src = '{}/{}'.format(base_url, subpath)
+  dest = os.path.join(temp_dir, subpath)
+  _MakeDirectory(os.path.dirname(dest))
+  subprocess.check_call(['gsutil.py', '-q', 'cp', src, dest])
+
+
+@contextlib.contextmanager
+def _DownloadSizeFiles(base_url, reports):
+  temp_dir = tempfile.mkdtemp()
+  try:
+    subpaths = set(x.after_size_file_subpath for x in reports)
+    subpaths.update(x.before_size_file_subpath
+                    for x in reports
+                    if x.before_size_file_subpath)
+    logging.warning('Downloading %d .size files', len(subpaths))
+    arg_tuples = ((p, temp_dir, base_url) for p in subpaths)
+    for _ in _Shard(_DownloadOneSizeFile, arg_tuples):
+      pass
+    yield temp_dir
+  finally:
+    shutil.rmtree(temp_dir)
+
+
+def _FetchExistingMilestoneReports():
+  milestones = subprocess.check_output(
+      ['gsutil.py', 'ls', '-R', _PUSH_URL + '*'])
+  for path in milestones.splitlines()[1:]:
+    report = Report.FromUrl(path)
+    if report:
+      yield report
+
+
+def _WriteMilestonesJson(path):
+  with open(path, 'w') as out_file:
+    pushed_reports_obj = {
+        'pushed': {
+            'apk': _DESIRED_APKS,
+            'cpu': _DESIRED_CPUS,
+            'version': _DESIRED_VERSIONS,
+        },
+    }
+    json.dump(pushed_reports_obj, out_file, sort_keys=True, indent=2)
+
+
+def _BuildOneReport(arg_tuples):
+  report, output_directory, size_file_directory = arg_tuples
+  ndjson_path = os.path.join(output_directory, report.ndjson_subpath)
+
+  _MakeDirectory(os.path.dirname(ndjson_path))
+  script = os.path.join(os.path.dirname(__file__), 'supersize')
+  after_size_file = os.path.join(size_file_directory,
+                                 report.after_size_file_subpath)
+  args = [script, 'html_report', after_size_file, ndjson_path]
+
+  if report.before_version:
+    before_size_file = os.path.join(size_file_directory,
+                                    report.before_size_file_subpath)
+    args += ['--diff-with', before_size_file]
+
+  subprocess.check_output(args, stderr=subprocess.STDOUT)
+
+
+def _CreateReportObjects(skip_existing):
+  desired_reports = set(_RequestedReports())
+  logging.warning('Querying storage bucket for existing reports.')
+  existing_reports = set(_FetchExistingMilestoneReports())
+  missing_reports = desired_reports - existing_reports
+  stale_reports = existing_reports - desired_reports
+  if stale_reports:
+    # Stale reports happen when we remove a version
+    # (e.g. update a beta to a stable).
+    # It's probably best to leave them in case people have linked to them.
+    logging.warning('Number of stale reports: %d', len(stale_reports))
+  if skip_existing:
+    return sorted(missing_reports)
+  return sorted(desired_reports)
+
+
+def main():
+  parser = argparse.ArgumentParser(description=__doc__)
+  parser.add_argument(
+      'directory', help='Directory to save report files to (must not exist).')
+  parser.add_argument(
+      '--size-file-bucket',
+      required=True,
+      help='GCS bucket to find size files in. (e.g. "gs://bucket/subdir")')
+  parser.add_argument(
+      '--sync',
+      action='store_true',
+      help='Sync data files to GCS (otherwise just prints out command to run).')
+  parser.add_argument(
+      '--skip-existing', action='store_true', help='Skip existing reports.')
+
+  args = parser.parse_args()
+  # Anything lower than WARNING gives screens full of supersize logs.
+  logging.basicConfig(
+      level=logging.WARNING,
+      format='%(levelname).1s %(relativeCreated)6d %(message)s')
+
+  size_file_bucket = args.size_file_bucket.rstrip('/')
+  if not size_file_bucket.startswith('gs://'):
+    parser.error('Size file bucket must start with gs://')
+
+  _MakeDirectory(args.directory)
+  if os.listdir(args.directory):
+    parser.error('Directory must be empty')
+
+  reports_to_make = _CreateReportObjects(args.skip_existing)
+  if not reports_to_make:
+    logging.warning('No reports need to be created (due to --skip-existing).')
+    return
+
+  with _DownloadSizeFiles(args.size_file_bucket, reports_to_make) as sizes_dir:
+    logging.warning('Generating %d reports.', len(reports_to_make))
+
+    arg_tuples = ((r, args.directory, sizes_dir) for r in reports_to_make)
+    for i, _ in enumerate(_Shard(_BuildOneReport, arg_tuples)):
+      sys.stdout.write('\rGenerated {} of {}'.format(i + 1,
+                                                     len(reports_to_make)))
+      sys.stdout.flush()
+    sys.stdout.write('\n')
+
+  _WriteMilestonesJson(os.path.join(args.directory, 'milestones.json'))
+
+  logging.warning('Reports saved to %s', args.directory)
+  cmd = [
+      'gsutil.py', '-m', 'rsync', '-J', '-a', 'public-read', '-r',
+      args.directory, _PUSH_URL,
+  ]
+
+  if args.sync:
+    subprocess.check_call(cmd)
+  else:
+    print
+    print 'Sync files by running:'
+    print '   ', ' '.join(cmd)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/binary_size/libsupersize/generate_milestone_report.py b/tools/binary_size/libsupersize/generate_milestone_report.py
deleted file mode 100755
index 955ed06b..0000000
--- a/tools/binary_size/libsupersize/generate_milestone_report.py
+++ /dev/null
@@ -1,273 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-"""Generate report files to view and/or compare (diff) milestones.
-
-Size files are located in a Google Cloud Storage bucket for various Chrome
-versions. This script generates various HTML report files to view a single
-milesone, or to compare two milestones with the same CPU and APK.
-
-Desired CPUs, APKs, and milestone versions are set in constants below. If
-specified by the --skip-existing flag, the script checks what HTML report files
-have already been uploaded to the GCS bucket, then works on generating the
-remaining desired files.
-
-Size files are fetched by streaming them from the source bucket, then the
-html_report module handles creating a report file to diff two size files.
-Reports are saved to a local directory, and once all reports are created they
-can be uploaded to the destination bucket.
-
-Reports can be uploaded automatically with the --sync flag. Otherwise, they can
-be uploaded at a later point.
-"""
-
-import argparse
-import codecs
-import collections
-import cStringIO
-import errno
-import itertools
-import json
-import logging
-import multiprocessing
-import os
-import re
-import subprocess
-
-import archive
-import diff
-import html_report
-
-
-PUSH_URL = 'gs://chrome-supersize/milestones/'
-REPORT_URL_TEMPLATE_VIEW = '{cpu}/{apk}/report_{version2}.ndjson'
-REPORT_URL_TEMPLATE_COMP = '{cpu}/{apk}/report_{version1}_{version2}.ndjson'
-
-DESIRED_CPUS = ['arm', 'arm_64']
-DESIRED_APKS = ['Monochrome.apk', 'ChromeModern.apk', 'AndroidWebview.apk']
-# Versions are manually gathered from
-# https://omahaproxy.appspot.com/history?os=android&channel=stable
-DESIRED_VERSIONS = [
-  '60.0.3112.116',
-  '61.0.3163.98',
-  '62.0.3202.84',
-  '63.0.3239.111',
-  '64.0.3282.137',
-  '65.0.3325.85',
-  '66.0.3359.158',
-  '67.0.3396.87',
-  '68.0.3440.85',
-  '69.0.3497.91',
-  '70.0.3538.64',
-  '71.0.3578.83',  # Beta
-  '72.0.3626.7',  # Beta
-]
-
-
-def _GetDesiredVersions(apk):
-  if apk != 'AndroidWebview.apk':
-    return DESIRED_VERSIONS
-  # Webview .size files do not exist before M71.
-  return [v for v in DESIRED_VERSIONS if int(v.split('.')[0]) >= 71]
-
-
-class Report(collections.namedtuple(
-  'Report', ['cpu', 'apk', 'version1', 'version2'])):
-  PUSH_URL_REGEX_VIEW = re.compile((PUSH_URL + REPORT_URL_TEMPLATE_VIEW).format(
-    cpu=r'(?P<cpu>[\w.]+)',
-    apk=r'(?P<apk>[\w.]+)',
-    version2=r'(?P<version2>[\w.]+)'
-  ))
-
-  PUSH_URL_REGEX_COMP = re.compile((PUSH_URL + REPORT_URL_TEMPLATE_COMP).format(
-    cpu=r'(?P<cpu>[\w.]+)',
-    apk=r'(?P<apk>[\w.]+)',
-    version1=r'(?P<version1>[\w.]+)',
-    version2=r'(?P<version2>[\w.]+)'
-  ))
-
-  @classmethod
-  def FromUrl(cls, url):
-    # Perform this match first since it's more restrictive.
-    match = cls.PUSH_URL_REGEX_COMP.match(url)
-    if match:
-      return cls(
-        match.group('cpu'),
-        match.group('apk'),
-        match.group('version1'),
-        match.group('version2'),
-      )
-    match = cls.PUSH_URL_REGEX_VIEW.match(url)
-    if match:
-      return cls(
-        match.group('cpu'),
-        match.group('apk'),
-        None,
-        match.group('version2'),
-      )
-    return None
-
-
-def _FetchExistingMilestoneReports():
-  milestones = subprocess.check_output(['gsutil.py', 'ls', '-R',
-                                         PUSH_URL + '*'])
-  for path in milestones.splitlines()[1:]:
-    report = Report.FromUrl(path)
-    if report:
-      yield report
-
-
-def _SizeInfoFromGsPath(path):
-  size_contents = subprocess.check_output(['gsutil.py', 'cat', path])
-  file_obj = cStringIO.StringIO(size_contents)
-  ret = archive.LoadAndPostProcessSizeInfo(path, file_obj=file_obj)
-  file_obj.close()
-  return ret
-
-
-def _PossibleReportFiles():
-  cpu_and_apk_combos = list(itertools.product(DESIRED_CPUS, DESIRED_APKS))
-  for cpu, apk in cpu_and_apk_combos:
-    apk_versions = _GetDesiredVersions(apk)
-    for version2 in apk_versions:
-      yield Report(cpu, apk, None, version2)
-    for i, version1 in enumerate(apk_versions):
-      for version2 in apk_versions[i + 1:]:
-        yield Report(cpu, apk, version1, version2)
-
-
-def _SetPushedReports(directory):
-  outpath = os.path.join(directory, 'milestones.json')
-  with codecs.open(outpath, 'w', encoding='ascii') as out_file:
-    pushed_reports_obj = {
-      'pushed': {
-        'cpu': DESIRED_CPUS,
-        'apk': DESIRED_APKS,
-        'version': DESIRED_VERSIONS,
-      },
-    }
-    json.dump(pushed_reports_obj, out_file)
-    out_file.write('\n')
-
-
-def _GetReportPaths(directory, template, report):
-  report_dict = report._asdict()
-  after_size_path = template.format(version=report.version2, **report_dict)
-  if report.version1 is None:
-    before_size_path = None
-    out_rel = os.path.join(directory,
-                           REPORT_URL_TEMPLATE_VIEW.format(**report_dict))
-  else:
-    before_size_path = template.format(version=report.version1,
-                                                   **report_dict)
-    out_rel = os.path.join(directory,
-                           REPORT_URL_TEMPLATE_COMP.format(**report_dict))
-  out_abs = os.path.abspath(out_rel)
-  return (before_size_path, after_size_path, out_abs)
-
-
-def _BuildReport(paths):
-  before_size_path, after_size_path, outpath = paths
-  try:
-    os.makedirs(os.path.dirname(outpath))
-  except OSError as e:
-    if e.errno != errno.EEXIST:
-      raise
-
-  size_info = _SizeInfoFromGsPath(after_size_path)
-  if before_size_path:
-    size_info = diff.Diff(_SizeInfoFromGsPath(before_size_path), size_info)
-
-  html_report.BuildReportFromSizeInfo(outpath, size_info, all_symbols=False)
-  return outpath
-
-
-def _BuildReports(directory, bucket, skip_existing):
-  try:
-    if os.listdir(directory):
-      raise Exception('Directory must be empty')
-  except OSError as e:
-    if e.errno == errno.ENOENT:
-      os.makedirs(directory)
-    else:
-      raise
-
-  # GCS URL template used to get size files.
-  template = bucket + '/{version}/{cpu}/{apk}.size'
-
-  def GetReportsToMake():
-    desired_reports = set(_PossibleReportFiles())
-    existing_reports = set(_FetchExistingMilestoneReports())
-    missing_reports = desired_reports - existing_reports
-    stale_reports = existing_reports - desired_reports
-    logging.info('Number of desired reports: %d' % len(desired_reports))
-    logging.info('Number of existing reports: %d' % len(existing_reports))
-    if stale_reports:
-      logging.warning('Number of stale reports: %d' % len(stale_reports))
-    if skip_existing:
-      logging.info('Generate %d missing reports:' % len(missing_reports))
-      return sorted(missing_reports)
-    logging.info('Generate all %d desired reports:' %
-                 len(desired_reports))
-    return sorted(desired_reports)
-
-  reports_to_make = GetReportsToMake()
-  if not reports_to_make:
-    return
-
-  paths = (_GetReportPaths(directory, template, r) for r in reports_to_make)
-
-  processes = min(len(reports_to_make), multiprocessing.cpu_count())
-  pool = multiprocessing.Pool(processes=processes)
-
-  for path in pool.imap_unordered(_BuildReport, paths):
-    logging.info('Saved %s', path)
-
-
-def main():
-  parser = argparse.ArgumentParser(description=__doc__)
-  parser.add_argument('directory',
-                      help='Directory to save report files to '
-                           '(must not exist).')
-  parser.add_argument('--size-file-bucket', required=True,
-                      help='GCS bucket to find size files in.'
-                           '(e.g. "gs://bucket/subdir")')
-  parser.add_argument('--sync', action='store_true',
-                      help='Sync data files to GCS '
-                           '(otherwise just prints out command to run).')
-  parser.add_argument('--skip-existing', action="store_true",
-                      help='Skip existing reports.')
-  parser.add_argument('-v',
-                      '--verbose',
-                      default=0,
-                      action='count',
-                      help='Verbose level (multiple times for more)')
-
-  args = parser.parse_args()
-  logging.basicConfig(level=logging.WARNING - args.verbose * 10,
-                      format='%(levelname).1s %(relativeCreated)6d %(message)s')
-
-  size_file_bucket = args.size_file_bucket
-  if not size_file_bucket.startswith('gs://'):
-    parser.error('Size file bucket must be located in Google Cloud Storage.')
-  elif size_file_bucket.endswith('/'):
-    # Remove trailing slash
-    size_file_bucket = size_file_bucket[:-1]
-
-  _BuildReports(args.directory, size_file_bucket,
-                skip_existing=args.skip_existing)
-  _SetPushedReports(args.directory)
-  logging.warning('Reports saved to %s', args.directory)
-  cmd = ['gsutil.py', '-m', 'rsync', '-J', '-a', 'public-read', '-r',
-         args.directory, PUSH_URL]
-
-  if args.sync:
-    subprocess.check_call(cmd)
-  else:
-    logging.warning('Sync files by running: \n%s', ' '.join(cmd))
-
-
-if __name__ == '__main__':
-  main()
diff --git a/tools/cfi/blacklist.txt b/tools/cfi/blacklist.txt
index 643bb19..8ebf793 100644
--- a/tools/cfi/blacklist.txt
+++ b/tools/cfi/blacklist.txt
@@ -137,6 +137,11 @@
 # Blacklist base::Callback due to https://crbug.com/845855
 fun:*FunctorTraits*
 
+# PipeWire due to https://crbug.com/926115
+src:*/include/spa/*
+src:*third_party/webrtc/modules/desktop_capture/linux/pipewire_stubs.cc
+src:*third_party/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc
+
 # Calls to auto-generated stubs by generate_stubs.py
 src:*audio/pulse/pulse_stubs.cc
 src:*media/gpu/vaapi/va_stubs.cc
@@ -158,9 +163,6 @@
 src:*ui/accessibility/platform/ax_platform_atk_hyperlink.cc
 
 src:*chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
-# Temporary needs both paths until the WebRTC changed is rolled in again
-# see https://bugs.chromium.org/p/chromium/issues/detail?id=901319#c14
-src:*third_party/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc
 src:*third_party/webrtc/modules/desktop_capture/linux/x_server_pixel_buffer.cc
 src:*media/cdm/*
 src:*third_party/swiftshader/*
diff --git a/tools/code_coverage/test_suite.txt b/tools/code_coverage/test_suite.txt
index a4ef9fd4..85601bd 100644
--- a/tools/code_coverage/test_suite.txt
+++ b/tools/code_coverage/test_suite.txt
@@ -7,6 +7,7 @@
 blink_common_unittests
 blink_heap_unittests
 blink_platform_unittests
+blink_unittests
 boringssl_crypto_tests
 boringssl_ssl_tests
 breakpad_unittests
@@ -81,7 +82,6 @@
 views_unittests
 viz_unittests
 vr_common_unittests
-webkit_unit_tests
 wm_unittests
 wtf_unittests
 zucchini_unittests
diff --git a/tools/determinism/deterministic_build_whitelist.pyl b/tools/determinism/deterministic_build_whitelist.pyl
index 6c3ef009..385e58a 100644
--- a/tools/determinism/deterministic_build_whitelist.pyl
+++ b/tools/determinism/deterministic_build_whitelist.pyl
@@ -60,6 +60,7 @@
     'blink_heap_unittests',
     'blink_platform_perftests',
     'blink_platform_unittests',
+    'blink_unittests',
     'bluetooth_metrics_hash',
     'browser_tests',
     'cacheinvalidation_unittests',
@@ -172,7 +173,6 @@
     'views_examples_with_content_exe',
     'views_unittests',
     'viz_benchmark',
-    'webkit_unit_tests',
     'wtf_unittests',
   ],
 
diff --git a/tools/mb/docs/design_spec.md b/tools/mb/docs/design_spec.md
index f3726ca..bbe816e 100644
--- a/tools/mb/docs/design_spec.md
+++ b/tools/mb/docs/design_spec.md
@@ -232,15 +232,15 @@
 Continuing the example given above, suppose we have the following build
 graph:
 
-* `blink_tests` is a meta target that depends on `webkit_unit_tests`,
+* `blink_tests` is a meta target that depends on `blink_unittests`,
   `wtf_unittests`, and `webkit_tests` and represents all of the targets
   needed to fully test Blink. Each of those is a separate test step.
 * `webkit_tests` is also a meta target; it depends on `content_shell`
   and `image_diff`.
 * `base_unittests` is a separate test binary.
-* `wtf_unittests` depends on `Assertions.cpp` and `AssertionsTest.cpp`.
-* `webkit_unit_tests` depends on `WebNode.cpp` and `WebNodeTest.cpp`.
-* `content_shell` depends on `WebNode.cpp` and `Assertions.cpp`.
+* `wtf_unittests` depends on `assertions.cc` and `assertions_test.cc`.
+* `blink_unittests` depends on `web_node.cc` and `web_node_test.cc`.
+* `content_shell` depends on `web_node.cc` and `assertions.cc`.
 * `base_unittests` depends on `logging.cc` and `logging_unittest.cc`.
 
 #### Example 1
@@ -248,10 +248,10 @@
 We wish to run 'wtf_unittests' and 'webkit_tests' on a bot, but not
 compile any additional targets.
 
-If a patch touches WebNode.cpp, then analyze gets as input:
+If a patch touches web_node.cc, then analyze gets as input:
 
     {
-      "files": ["WebNode.cpp"],
+      "files": ["web_node.cc"],
       "test_targets": ["wtf_unittests", "webkit_tests"],
       "additional_compile_targets": []
     }
@@ -260,7 +260,7 @@
 
     {
       "status": "Found dependency",
-      "compile_targets": ["webkit_unit_tests"],
+      "compile_targets": ["blink_unittests"],
       "test_targets": ["webkit_tests"]
     }
 
@@ -274,7 +274,7 @@
 We pass as input:
 
     {
-      "files": ["WebNode.cpp"],
+      "files": ["web_node.cc"],
       "test_targets": ["wtf_unittests"],
       "additional_compile_targets": ["blink_tests"]
     }
@@ -283,7 +283,7 @@
 
     {
       "status": "Found dependency",
-      "compile_targets": ["webkit_unit_tests"],
+      "compile_targets": ["blink_unittests"],
       "test_targets": []
     }
 
@@ -298,7 +298,7 @@
 Input:
 
     {
-      "files": ["WebNode.cpp"],
+      "files": ["web_node.cc"],
       "test_targets": [],
       "additional_compile_targets": ["all"]
     }
@@ -307,7 +307,7 @@
 
     {
       "status": "Found dependency",
-      "compile_targets": ["webkit_unit_tests", "content_shell"],
+      "compile_targets": ["blink_unittests", "content_shell"],
       "test_targets": []
     }
 
@@ -327,7 +327,7 @@
 
     {
       "status": "Found dependency (all)",
-      "compile_targets": ["webkit_unit_tests", "wtf_unittests"],
+      "compile_targets": ["blink_unittests", "wtf_unittests"],
       "test_targets": ["wtf_unittests"]
     }
 
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index e7fc99d4..d448098 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -311,7 +311,8 @@
       'Chromium Mac Goma RBE Staging (clobber)': 'release_bot',
       'Chromium Mac Goma RBE Staging': 'release_bot',
       'Chromium Mac Goma RBE Staging (dbg)': 'debug_bot',
-      'Chromium Win Goma RBE ToT': 'release_bot',
+      # Same as CrWinGomaStaging.
+      'Chromium Win Goma RBE ToT': 'release_bot_x86_minimal_symbols',
       'Chromium Android ARM 32-bit Goma RBE ToT': 'android_release_bot_minimal_symbols',
       'Chromium Android ARM 32-bit Goma RBE Staging': 'android_release_bot_minimal_symbols',
       'Chromium Android ARM 32-bit Goma RBE Prod': 'android_release_bot_minimal_symbols',
@@ -647,6 +648,10 @@
       'win_angle_x64_rel_ng': 'gpu_fyi_tests_release_trybot',
     },
 
+    'tryserver.chromium.chrome': {
+      'google-chrome-chromeos': 'official_goma_chromeos',
+    },
+
     'tryserver.chromium.chromiumos': {
       # TODO(crbug.com/913750): Enable DCHECKS on the two amd64-generic bots
       # when the PFQ has it enabled.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b00d31e..ae7ca53 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -17895,6 +17895,9 @@
   <int value="1315" label="PASSWORDSPRIVATE_CHANGESAVEDPASSWORD"/>
   <int value="1316" label="AUTOTESTPRIVATE_SETWHITELISTEDPREF"/>
   <int value="1317" label="SAFEBROWSINGPRIVATE_GETREFERRERCHAIN"/>
+  <int value="1318" label="DECLARATIVENETREQUEST_ADDDYNAMICRULES"/>
+  <int value="1319" label="DECLARATIVENETREQUEST_REMOVEDYNAMICRULES"/>
+  <int value="1320" label="DECLARATIVENETREQUEST_GETDYNAMICRULES"/>
 </enum>
 
 <enum name="ExtensionIconState">
diff --git a/tools/traffic_annotation/auditor/tests/gn_list_positive.txt b/tools/traffic_annotation/auditor/tests/gn_list_positive.txt
index 19c133c..e34f37f5 100644
--- a/tools/traffic_annotation/auditor/tests/gn_list_positive.txt
+++ b/tools/traffic_annotation/auditor/tests/gn_list_positive.txt
@@ -1,11 +1,11 @@
 //:All
 //:blink_tests
+//:blink_web_tests
+//:blink_web_tests_exparchive
 //:chromium_builder_asan
 //:chromium_builder_perf
 //:gn_all
-//:run_webkit_tests
-//:webkit_layout_tests
-//:webkit_layout_tests_exparchive
+//:run_web_tests
 //apps:apps
 //apps:test_support
 //apps/ui/views:views
diff --git a/ui/accessibility/PRESUBMIT.py b/ui/accessibility/PRESUBMIT.py
index 4465933..b3038b8 100644
--- a/ui/accessibility/PRESUBMIT.py
+++ b/ui/accessibility/PRESUBMIT.py
@@ -70,7 +70,8 @@
                       automation_enums,
                       automation_enum_name,
                       errs,
-                      output_api):
+                      output_api,
+                      strict_ordering=False):
   if ax_enum_name not in ax_enums:
     errs.append(output_api.PresubmitError(
         'Expected %s to have an enum named %s' % (AX_MOJOM, ax_enum_name)))
@@ -82,6 +83,25 @@
     return
   src = ax_enums[ax_enum_name]
   dst = automation_enums[automation_enum_name]
+  if strict_ordering and len(src) != len(dst):
+    errs.append(output_api.PresubmitError(
+        'Expected %s to have the same number of items as %s' % (
+            automation_enum_name, ax_enum_name)))
+    return
+
+  if strict_ordering:
+    for index, value in enumerate(src):
+      lower_value = InitialLowerCamelCase(value)
+      if lower_value != dst[index]:
+        errs.append(output_api.PresubmitError(
+            ('At index %s in enums, unexpected ordering around %s.%s ' +
+            'and %s.%s in %s and %s') % (
+                index, ax_enum_name, lower_value,
+                automation_enum_name, dst[index],
+                AX_MOJOM, AUTOMATION_IDL)))
+        return
+    return
+
   for value in src:
     lower_value = InitialLowerCamelCase(value)
     if lower_value in dst:
@@ -114,9 +134,9 @@
   CheckMatchingEnum(ax_enums, 'Role', automation_enums, 'RoleType', errs,
                     output_api)
   CheckMatchingEnum(ax_enums, 'State', automation_enums, 'StateType', errs,
-                    output_api)
+                    output_api, strict_ordering=True)
   CheckMatchingEnum(ax_enums, 'Action', automation_enums, 'ActionType', errs,
-                    output_api)
+                    output_api, strict_ordering=True)
   CheckMatchingEnum(ax_enums, 'Event', automation_enums, 'EventType', errs,
                     output_api)
   CheckMatchingEnum(ax_enums, 'NameFrom', automation_enums, 'NameFromType',
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom
index f8515bb..d736341 100644
--- a/ui/accessibility/ax_enums.mojom
+++ b/ui/accessibility/ax_enums.mojom
@@ -340,6 +340,9 @@
    // Return the content of this image object in the image_data attribute.
   kGetImageData,
 
+  // Gets the bounding rect for a range of text.
+  kGetTextLocation,
+
    // Given a point, find the object it corresponds to and fire a
    // |AXActionData.hit_test_event_to_fire| event on it in response.
   kHitTest,
@@ -390,9 +393,7 @@
    // Replace the value of the control with AXActionData::value and
    // reset the selection, if applicable.
   kSetValue,
-
   kShowContextMenu,
-  kGetTextLocation,
 };
 
 enum ActionFlags {
diff --git a/ui/chromeos/events/event_rewriter_chromeos.cc b/ui/chromeos/events/event_rewriter_chromeos.cc
index 2eb6122..167102e 100644
--- a/ui/chromeos/events/event_rewriter_chromeos.cc
+++ b/ui/chromeos/events/event_rewriter_chromeos.cc
@@ -765,11 +765,10 @@
 
   // Remapping based on DomCode.
   switch (incoming.code) {
-    // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock
-    // is pressed (with one exception: when
-    // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates
-    // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7).
-    case ui::DomCode::F16:
+    // On Chrome OS, Caps_Lock with Mod3Mask is sent when Caps Lock is pressed
+    // (with one exception: when IsISOLevel5ShiftUsedByCurrentInputMethod() is
+    // true, the key generates XK_ISO_Level3_Shift with Mod3Mask, not
+    // Caps_Lock).
     case ui::DomCode::CAPS_LOCK:
       // This key is already remapped to Mod3 in remapping based on DomKey. Skip
       // more remapping.
@@ -817,13 +816,10 @@
     state->key = remapped_key->result.key;
     incoming.flags |= characteristic_flag;
     characteristic_flag = remapped_key->flag;
-    if (incoming.key_code == ui::VKEY_CAPITAL ||
-        incoming.key_code == ui::VKEY_F16) {
+    if (incoming.key_code == ui::VKEY_CAPITAL) {
       // Caps Lock is rewritten to another key event, remove EF_CAPS_LOCK_ON
       // flag to prevent the keyboard's Caps Lock state being synced to the
-      // rewritten key event's flag in InputMethodChromeOS. (Caps Lock key on an
-      // external keyboard generates F16 which is treated as Caps Lock and then
-      // rewritten.)
+      // rewritten key event's flag in InputMethodChromeOS.
       incoming.flags &= ~ui::EF_CAPS_LOCK_ON;
     }
     if (remapped_key->remap_to == ui::chromeos::ModifierKey::kCapsLockKey)
diff --git a/ui/events/ozone/layout/layout_util.cc b/ui/events/ozone/layout/layout_util.cc
index 5be53ec..eaafb10e 100644
--- a/ui/events/ozone/layout/layout_util.cc
+++ b/ui/events/ozone/layout/layout_util.cc
@@ -15,10 +15,6 @@
       return EF_ALT_DOWN;
     case DomKey::ALT_GRAPH:
       return EF_ALTGR_DOWN;
-    // ChromeOS uses F16 to represent CapsLock before the rewriting stage,
-    // based on the historical X11 implementation.
-    // TODO post-X11: Switch to use CapsLock uniformly.
-    case DomKey::F16:
     case DomKey::CAPS_LOCK:
       return EF_CAPS_LOCK_ON;
     case DomKey::CONTROL:
diff --git a/ui/file_manager/externs/app_window_common.js b/ui/file_manager/externs/app_window_common.js
index bfb0db2..84bbdcd13 100644
--- a/ui/file_manager/externs/app_window_common.js
+++ b/ui/file_manager/externs/app_window_common.js
@@ -13,3 +13,8 @@
  * @type {string}
  */
 Window.prototype.appInitialURL;
+
+/**
+ * @type {function()}
+ */
+Window.prototype.reload = function() {};
diff --git a/ui/file_manager/file_manager/background/js/app_window_wrapper.js b/ui/file_manager/file_manager/background/js/app_window_wrapper.js
index 2570b54..0088362 100644
--- a/ui/file_manager/file_manager/background/js/app_window_wrapper.js
+++ b/ui/file_manager/file_manager/background/js/app_window_wrapper.js
@@ -52,7 +52,7 @@
  * @param {string} url Initialize URL that the window has.
  * @return {string} Key of window geometry preferences.
  */
-AppWindowWrapper.makeGeometryKey = function(url) {
+AppWindowWrapper.makeGeometryKey = url => {
   return 'windowGeometry' + ':' + url;
 };
 
@@ -99,10 +99,10 @@
 
   // Restore maximized windows, to avoid hiding them to tray, which can be
   // confusing for users.
-  this.queue.run(function(callback) {
+  this.queue.run(callback => {
     for (let index = 0; index < similarWindows.length; index++) {
       if (similarWindows[index].isMaximized()) {
-        const createWindowAndRemoveListener = function() {
+        const createWindowAndRemoveListener = () => {
           similarWindows[index].onRestored.removeListener(
               createWindowAndRemoveListener);
           callback();
@@ -120,20 +120,20 @@
   // Obtains the last geometry and window state (maximized or not).
   let lastBounds;
   let isMaximized = false;
-  this.queue.run(function(callback) {
+  this.queue.run(callback => {
     const boundsKey = AppWindowWrapper.makeGeometryKey(this.url_);
     const maximizedKey = AppWindowWrapper.MAXIMIZED_KEY_;
-    chrome.storage.local.get([boundsKey, maximizedKey], function(preferences) {
+    chrome.storage.local.get([boundsKey, maximizedKey], preferences => {
       if (!chrome.runtime.lastError) {
         lastBounds = preferences[boundsKey];
         isMaximized = preferences[maximizedKey];
       }
       callback();
     });
-  }.bind(this));
+  });
 
   // Closure creating the window, once all preprocessing tasks are finished.
-  this.queue.run(function(callback) {
+  this.queue.run(callback => {
     // Apply the last bounds.
     if (lastBounds) {
       this.options_.bounds = lastBounds;
@@ -145,7 +145,7 @@
     }
 
     // Create a window.
-    chrome.app.window.create(this.url_, this.options_, function(appWindow) {
+    chrome.app.window.create(this.url_, this.options_, appWindow => {
       // Exit full screen state if it's created as a full screen window.
       if (appWindow.isFullscreen()) {
         appWindow.restore();
@@ -159,13 +159,13 @@
       }
       this.window_ = appWindow;
       callback();
-    }.bind(this));
-  }.bind(this));
+    });
+  });
 
   // After creating.
-  this.queue.run(function(callback) {
+  this.queue.run(callback => {
     // If there is another window in the same position, shift the window.
-    const makeBoundsKey = function(bounds) {
+    const makeBoundsKey = bounds => {
       return bounds.left + '/' + bounds.top;
     };
     const notAvailablePositions = {};
@@ -190,7 +190,9 @@
       candidateBounds.top = nextBottom >= screen.availHeight ?
           nextBottom % screen.availHeight : nextTop;
     }
-    this.window_.moveTo(candidateBounds.left, candidateBounds.top);
+    this.window_.moveTo(
+        /** @type {number} */ (candidateBounds.left),
+        /** @type {number} */ (candidateBounds.top));
 
     // Save the properties.
     const appWindow = this.window_;
@@ -213,7 +215,7 @@
       opt_callback();
     }
     callback();
-  }.bind(this));
+  });
 };
 
 /**
@@ -237,7 +239,7 @@
 
   // Updates preferences.
   if (contentWindow.saveOnExit) {
-    contentWindow.saveOnExit.forEach(function(entry) {
+    contentWindow.saveOnExit.forEach(entry => {
       appUtil.AppCache.update(entry.key, entry.value);
     });
   }
@@ -302,7 +304,7 @@
 
   // If the window is already opened, reload the window.
   // The queue is used to wait until the window is opened.
-  this.queue.run(function(nextStep) {
+  this.queue.run(nextStep => {
     this.window_.contentWindow.appState = appState;
     this.window_.contentWindow.appReopen = reopen;
     if (!this.window_.contentWindow.reload) {
@@ -318,7 +320,7 @@
       opt_callback();
     }
     nextStep();
-  }.bind(this));
+  });
 };
 
 /**
@@ -326,7 +328,7 @@
  * @param {function()=} opt_callback Completion callback.
  */
 SingletonAppWindowWrapper.prototype.reopen = function(opt_callback) {
-  chrome.storage.local.get(this.id_, function(items) {
+  chrome.storage.local.get(this.id_, items => {
     const value = items[this.id_];
     if (!value) {
       opt_callback && opt_callback();
@@ -341,5 +343,5 @@
       return;
     }
     this.launch(appState, true, opt_callback);
-  }.bind(this));
+  });
 };
diff --git a/ui/file_manager/file_manager/background/js/app_windows.js b/ui/file_manager/file_manager/background/js/app_windows.js
index c75f62a..421dc33 100644
--- a/ui/file_manager/file_manager/background/js/app_windows.js
+++ b/ui/file_manager/file_manager/background/js/app_windows.js
@@ -13,7 +13,7 @@
  * @param {string} url URL that the obtained windows have.
  * @return {Array<chrome.app.window.AppWindow>} List of similar windows.
  */
-window.getSimilarWindows = function(url) {
+window.getSimilarWindows = url => {
   const result = [];
   for (const appID in window.appWindows) {
     if (window.appWindows[appID].contentWindow.appInitialURL === url) {
diff --git a/ui/file_manager/file_manager/background/js/background.js b/ui/file_manager/file_manager/background/js/background.js
index 9bfaf42..192b2d90 100644
--- a/ui/file_manager/file_manager/background/js/background.js
+++ b/ui/file_manager/file_manager/background/js/background.js
@@ -102,32 +102,32 @@
       this.onContextMenuClicked_.bind(this));
 
   // Initialize string and volume manager related stuffs.
-  this.initializationPromise_.then(function(strings) {
+  this.initializationPromise_.then(strings => {
     this.stringData = strings;
     this.initContextMenu_();
 
-    volumeManagerFactory.getInstance().then(function(volumeManager) {
+    volumeManagerFactory.getInstance().then(volumeManager => {
       volumeManager.addEventListener(
           VolumeManagerCommon.VOLUME_ALREADY_MOUNTED,
           this.handleViewEvent_.bind(this));
 
       this.crostini.init(volumeManager);
       this.crostini.listen();
-    }.bind(this));
+    });
 
     this.fileOperationManager = new FileOperationManagerImpl();
     this.fileOperationHandler_ = new FileOperationHandler(
         this.fileOperationManager, this.progressCenter);
-  }.bind(this));
+  });
 
   // Handle newly mounted FSP file systems. Workaround for crbug.com/456648.
   // TODO(mtomasz): Replace this hack with a proper solution.
   chrome.fileManagerPrivate.onMountCompleted.addListener(
       this.onMountCompleted_.bind(this));
 
-  launcher.queue.run(function(callback) {
+  launcher.queue.run(callback => {
     this.initializationPromise_.then(callback);
-  }.bind(this));
+  });
 }
 
 FileBrowserBackgroundImpl.prototype.__proto__ = BackgroundBase.prototype;
@@ -163,12 +163,12 @@
 FileBrowserBackgroundImpl.prototype.handleViewEventInternal_ = function(event) {
   volumeManagerFactory.getInstance()
       .then(
-          (/**
-           * Retrieves the root file entry of the volume on the requested
-           * device.
-           * @param {!VolumeManager} volumeManager
-           */
-          function(volumeManager) {
+          /**
+          * Retrieves the root file entry of the volume on the requested
+          * device.
+          * @param {!VolumeManager} volumeManager
+          */
+          volumeManager => {
             if (event.devicePath) {
               let volume = volumeManager.findByDevicePath(event.devicePath);
               if (volume) {
@@ -186,17 +186,17 @@
             } else {
               console.error('Got view event with no actionable destination.');
             }
-          }).bind(this));
+          });
 };
 
 /**
  * Retrieves the root file entry of the volume on the requested device.
  *
  * @param {!string} volumeId ID of the volume to navigate to.
- * @return {!Promise<VolumeInfo>}
+ * @return {!Promise<!VolumeInfo>}
  * @private
  */
-FileBrowserBackgroundImpl.prototype.retrieveVolumeInfo_ = function(volumeId) {
+FileBrowserBackgroundImpl.prototype.retrieveVolumeInfo_ = volumeId => {
   return volumeManagerFactory.getInstance().then(
       (/**
         * @param {!VolumeManager} volumeManager
@@ -219,9 +219,9 @@
  */
 FileBrowserBackgroundImpl.prototype.navigateToVolumeWhenReady_ = function(
     volumeId, opt_directoryPath) {
-  this.retrieveVolumeInfo_(volumeId).then(function(volume) {
+  this.retrieveVolumeInfo_(volumeId).then(volume => {
     this.navigateToVolumeRoot_(volume, opt_directoryPath);
-  }.bind(this));
+  });
 };
 
 /**
@@ -234,9 +234,9 @@
  */
 FileBrowserBackgroundImpl.prototype.navigateToVolumeInFocusedWindowWhenReady_ =
     function(volumeId, opt_directoryPath) {
-  this.retrieveVolumeInfo_(volumeId).then(function(volume) {
+  this.retrieveVolumeInfo_(volumeId).then(volume => {
     this.navigateToVolumeInFocusedWindow_(volume, opt_directoryPath);
-  }.bind(this));
+  });
 };
 
 /**
@@ -248,9 +248,8 @@
  * @return {!Promise<!DirectoryEntry>}
  * @private
  */
-FileBrowserBackgroundImpl.prototype.retrieveEntryInVolume_ = function(
-    volume, opt_directoryPath) {
-  return volume.resolveDisplayRoot().then(function(root) {
+FileBrowserBackgroundImpl.prototype.retrieveEntryInVolume_ = (volume, opt_directoryPath) => {
+  return volume.resolveDisplayRoot().then(root => {
     if (opt_directoryPath) {
       return new Promise(
           root.getDirectory.bind(root, opt_directoryPath, {create: false}));
@@ -275,7 +274,7 @@
            * Launches app opened on {@code directory}.
            * @param {DirectoryEntry} directory
            */
-          function(directory) {
+          directory => {
             launcher.launchFileManager(
                 {currentDirectoryURL: directory.toURL()},
                 /* App ID */ undefined, LaunchType.FOCUS_SAME_OR_CREATE);
@@ -295,10 +294,10 @@
   this.retrieveEntryInVolume_(volume, opt_directoryPath)
       .then(function(directoryEntry) {
         if (directoryEntry) {
-          volumeManagerFactory.getInstance().then(function(volumeManager) {
+          volumeManagerFactory.getInstance().then(volumeManager => {
             volumeManager.dispatchEvent(
                 VolumeManagerCommon.createArchiveOpenedEvent(directoryEntry));
-          }.bind(this));
+          });
         }
       });
 };
@@ -327,7 +326,7 @@
   if (window.IN_TEST) {
     dialogWindow.IN_TEST = true;
   }
-  dialogWindow.addEventListener('pagehide', function() {
+  dialogWindow.addEventListener('pagehide', () => {
     delete window.background.dialogs[id];
   });
 }
@@ -339,7 +338,7 @@
  * @param {Object} details Details object.
  * @private
  */
-FileBrowserBackgroundImpl.prototype.onExecute_ = function(action, details) {
+FileBrowserBackgroundImpl.prototype.onExecute_ = (action, details) => {
   const appState = {
     params: {action: action},
     // It is not allowed to call getParent() here, since there may be
@@ -365,11 +364,11 @@
  */
 FileBrowserBackgroundImpl.prototype.onLaunched_ = function() {
   metrics.startInterval('Load.BackgroundLaunch');
-  this.initializationPromise_.then(function() {
+  this.initializationPromise_.then(() => {
     if (nextFileManagerWindowID == 0) {
       // The app just launched. Remove window state records that are not needed
       // any more.
-      chrome.storage.local.get(function(items) {
+      chrome.storage.local.get(items => {
         for (const key in items) {
           if (items.hasOwnProperty(key)) {
             if (key.match(FILES_ID_PATTERN)) {
@@ -380,7 +379,7 @@
       });
     }
     launcher.launchFileManager(
-        null, undefined, LaunchType.FOCUS_ANY_OR_CREATE, function() {
+        null, undefined, LaunchType.FOCUS_ANY_OR_CREATE, () => {
           metrics.recordInterval('Load.BackgroundLaunch');
         });
   });
@@ -396,7 +395,7 @@
  * @param {MessageSender} sender
  */
 FileBrowserBackgroundImpl.prototype.onExternalMessageReceived_ =
-    function(message, sender) {
+    (message, sender) => {
   if ('id' in sender && sender.id === GPLUS_PHOTOS_APP_ID) {
     importer.handlePhotosAppMessage(message);
   }
@@ -407,9 +406,9 @@
  * @private
  * @override
  */
-FileBrowserBackgroundImpl.prototype.onRestarted_ = function() {
+FileBrowserBackgroundImpl.prototype.onRestarted_ = () => {
   // Reopen file manager windows.
-  chrome.storage.local.get(function(items) {
+  chrome.storage.local.get(items => {
     for (const key in items) {
       if (items.hasOwnProperty(key)) {
         const match = key.match(FILES_ID_PATTERN);
@@ -418,7 +417,7 @@
           const id = Number(match[1]);
           try {
             const appState = /** @type {Object} */ (JSON.parse(items[key]));
-            launcher.launchFileManager(appState, id, undefined, function() {
+            launcher.launchFileManager(appState, id, undefined, () => {
               metrics.recordInterval('Load.BackgroundRestart');
             });
           } catch (e) {
@@ -439,7 +438,7 @@
   if (info.menuItemId == 'new-window') {
     // Find the focused window (if any) and use it's current url for the
     // new window. If not found, then launch with the default url.
-    this.findFocusedWindow_().then(function(key) {
+    this.findFocusedWindow_().then(key => {
       if (!key) {
         launcher.launchFileManager();
         return;
@@ -450,7 +449,7 @@
             contentWindow.appState.currentDirectoryURL
       };
       launcher.launchFileManager(appState);
-    }).catch(function(error) {
+    }).catch(error => {
       console.error(error.stack || error);
     });
   }
@@ -463,8 +462,8 @@
  *     window, or null if not found.
  * @private
  */
-FileBrowserBackgroundImpl.prototype.findFocusedWindow_ = function() {
-  return new Promise(function(fulfill, reject) {
+FileBrowserBackgroundImpl.prototype.findFocusedWindow_ = () => {
+  return new Promise((fulfill, reject) => {
     for (const key in window.appWindows) {
       try {
         if (window.appWindows[key].contentWindow.isFocused()) {
@@ -501,7 +500,7 @@
   // If there is no focused window, then create a new one opened on the
   // mounted volume.
   this.findFocusedWindow_()
-      .then(function(key) {
+      .then(key => {
         let statusOK = event.status === 'success' ||
             event.status === 'error_path_already_mounted';
         let volumeTypeOK = event.volumeMetadata.volumeType ===
@@ -511,8 +510,8 @@
             event.volumeMetadata.mountContext === 'user' && volumeTypeOK) {
           this.navigateToVolumeWhenReady_(event.volumeMetadata.volumeId);
         }
-      }.bind(this))
-      .catch(function(error) {
+      })
+      .catch(error => {
         console.error(error.stack || error);
       });
 };
@@ -521,7 +520,7 @@
  * Initializes the context menu. Recreates if already exists.
  * @private
  */
-FileBrowserBackgroundImpl.prototype.initContextMenu_ = function() {
+FileBrowserBackgroundImpl.prototype.initContextMenu_ = () => {
   try {
     // According to the spec [1], the callback is optional. But no callback
     // causes an error for some reason, so we call it with null-callback to
@@ -529,7 +528,7 @@
     // Also, we read the runtime.lastError here not to output the message on the
     // console as an unchecked error.
     // - [1] https://developer.chrome.com/extensions/contextMenus#method-remove
-    chrome.contextMenus.remove('new-window', function() {
+    chrome.contextMenus.remove('new-window', () => {
       const ignore = chrome.runtime.lastError;
     });
   } catch (ignore) {
diff --git a/ui/file_manager/file_manager/background/js/background_base.js b/ui/file_manager/file_manager/background/js/background_base.js
index 58b012d..64b2287 100644
--- a/ui/file_manager/file_manager/background/js/background_base.js
+++ b/ui/file_manager/file_manager/background/js/background_base.js
@@ -18,8 +18,8 @@
   this.dialogs = {};
 
   // Initializes the strings. This needs for the volume manager.
-  this.initializationPromise_ = new Promise(function(fulfill, reject) {
-    chrome.fileManagerPrivate.getStrings(function(stringData) {
+  this.initializationPromise_ = new Promise((fulfill, reject) => {
+    chrome.fileManagerPrivate.getStrings(stringData => {
       if (chrome.runtime.lastError) {
         console.error(chrome.runtime.lastError.message);
         return;
@@ -49,13 +49,13 @@
     return;
   }
 
-  this.initializationPromise_.then(function() {
+  this.initializationPromise_.then(() => {
     // Volume list needs to be initialized (more precisely,
     // chrome.fileSystem.requestFileSystem needs to be called to grant access)
     // before resolveIsolatedEntries().
     return volumeManagerFactory.getInstance();
-  }).then(function() {
-    const isolatedEntries = launchData.items.map(function(item) {
+  }).then(() => {
+    const isolatedEntries = launchData.items.map(item => {
       return item.entry;
     });
 
@@ -65,13 +65,13 @@
     // their parent directory.
     chrome.fileManagerPrivate.resolveIsolatedEntries(
         isolatedEntries,
-        function(externalEntries) {
+        externalEntries => {
           const urls = util.entriesToURLs(externalEntries);
           if (this.launchHandler_) {
             this.launchHandler_(urls);
           }
-        }.bind(this));
-  }.bind(this));
+        });
+  });
 };
 
 /**
@@ -85,5 +85,5 @@
 /**
  * Called when an app is restarted.
  */
-BackgroundBase.prototype.onRestarted_ = function() {
+BackgroundBase.prototype.onRestarted_ = () => {
 };
diff --git a/ui/file_manager/file_manager/background/js/device_handler.js b/ui/file_manager/file_manager/background/js/device_handler.js
index 956128e..be9441b 100644
--- a/ui/file_manager/file_manager/background/js/device_handler.js
+++ b/ui/file_manager/file_manager/background/js/device_handler.js
@@ -214,9 +214,9 @@
  */
 DeviceHandler.Notification.prototype.show = function(devicePath, opt_message) {
   const notificationId = this.makeId_(devicePath);
-  this.queue_.run(function(callback) {
+  this.queue_.run(callback => {
     this.showInternal_(notificationId, opt_message || null, callback);
-  }.bind(this));
+  });
   return notificationId;
 };
 
@@ -228,13 +228,13 @@
 DeviceHandler.Notification.prototype.showOnce = function(devicePath) {
   const notificationId = this.makeId_(devicePath);
   this.queue_.run(function(callback) {
-    chrome.notifications.getAll(function(idList) {
+    chrome.notifications.getAll(idList => {
       if (idList.indexOf(notificationId) !== -1) {
         callback();
         return;
       }
       this.showInternal_(notificationId, null, callback);
-    }.bind(this));
+    });
   });
 };
 
@@ -268,9 +268,9 @@
  * @param {string} devicePath Device path.
  */
 DeviceHandler.Notification.prototype.hide = function(devicePath) {
-  this.queue_.run(function(callback) {
+  this.queue_.run(callback => {
     chrome.notifications.clear(this.makeId_(devicePath), callback);
-  }.bind(this));
+  });
 };
 
 /**
@@ -382,7 +382,7 @@
     return;
   }
 
-  const getFirstStatus = function(event) {
+  const getFirstStatus = event => {
     if (event.status === 'success') {
       return DeviceHandler.MountStatus.SUCCESS;
     } else if (event.volumeMetadata.isParentDevice) {
@@ -500,11 +500,11 @@
            * @return {!Promise<!DirectoryEntry>} The root directory
            *     of the volume.
            */
-          function(volumeInfo) {
+          volumeInfo => {
             return importer.importEnabled()
                 .then(
                     /** @param {boolean} enabled */
-                    function(enabled) {
+                    enabled => {
                       if (enabled && importer.isEligibleVolume(volumeInfo)) {
                         return volumeInfo.resolveDisplayRoot();
                       }
@@ -516,30 +516,28 @@
            * @param {!DirectoryEntry} root
            * @return {!Promise<!DirectoryEntry>}
            */
-          function(root) {
+          root => {
             return importer.getMediaDirectory(root);
           })
-      .then((/**
-              * @param {!DirectoryEntry} directory
-              * @this {DeviceHandler}
-              */
-             function(directory) {
-               return importer.isPhotosAppImportEnabled().then(
-                   (/**
-                     * @param {boolean} appEnabled
-                     * @this {DeviceHandler}
-                     */
-                    function(appEnabled) {
-                      // We don't want to auto-open two windows when a user
-                      // inserts a removable device.  Only open Files app if
-                      // auto-import is disabled in Photos app.
-                      if (!appEnabled) {
-                        this.openMediaDirectory_(
-                            metadata.volumeId, null, directory.fullPath);
-                      }
-                    }).bind(this));
-             }).bind(this))
-      .catch(function(error) {
+      .then(/**
+   * @param {!DirectoryEntry} directory
+   */
+  directory => {
+    return importer.isPhotosAppImportEnabled().then(
+        /**
+         * @param {boolean} appEnabled
+         */
+        appEnabled => {
+          // We don't want to auto-open two windows when a user
+          // inserts a removable device.  Only open Files app if
+          // auto-import is disabled in Photos app.
+          if (!appEnabled) {
+            this.openMediaDirectory_(
+                metadata.volumeId, null, directory.fullPath);
+          }
+        });
+  })
+      .catch(error => {
         if (metadata.deviceType && metadata.devicePath) {
           if (metadata.isReadOnly && !metadata.isReadOnlyRemovableDevice) {
             DeviceHandler.Notification.DEVICE_NAVIGATION_READONLY_POLICY.show(
@@ -552,7 +550,7 @@
       });
 };
 
-DeviceHandler.prototype.onUnmount_ = function(event) {
+DeviceHandler.prototype.onUnmount_ = event => {
   DeviceHandler.Notification.DEVICE_NAVIGATION.hide(
       /** @type {string} */ (event.devicePath));
 };
@@ -577,10 +575,10 @@
   const type = id.substr(0, pos);
   const devicePath = id.substr(pos + 1);
   if (type === 'deviceNavigation' || type === 'deviceFail') {
-    chrome.notifications.clear(id, function() {});
+    chrome.notifications.clear(id, () => {});
     this.openMediaDirectory_(null, devicePath, null);
   } else if (type === 'deviceImport') {
-    chrome.notifications.clear(id, function() {});
+    chrome.notifications.clear(id, () => {});
     this.openMediaDirectory_(null, devicePath, 'DCIM');
   }
 };
diff --git a/ui/file_manager/file_manager/background/js/device_handler_unittest.js b/ui/file_manager/file_manager/background/js/device_handler_unittest.js
index d887442..0ca85d4 100644
--- a/ui/file_manager/file_manager/background/js/device_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/device_handler_unittest.js
@@ -49,7 +49,7 @@
   });
 
   reportPromise(
-      mockChrome.notifications.resolver.promise.then(function(notifications) {
+      mockChrome.notifications.resolver.promise.then(notifications => {
         assertEquals(1, Object.keys(notifications).length);
         const options = notifications['deviceNavigation:/device/path'];
         assertEquals('REMOVABLE_DEVICE_NAVIGATION_MESSAGE', options.message);
@@ -73,7 +73,7 @@
 
   // Handle media device navigation requests.
   deviceHandler.addEventListener(
-      DeviceHandler.VOLUME_NAVIGATION_REQUESTED, function(event) {
+      DeviceHandler.VOLUME_NAVIGATION_REQUESTED, event => {
         resolver.resolve(event);
       });
 
@@ -86,7 +86,7 @@
 
   reportPromise(
       resolver.promise.then(
-          function(event) {
+          event => {
             assertEquals('blabbity', event.volumeId);
           }),
       callback);
@@ -107,7 +107,7 @@
 
   // Handle media device navigation requests.
   deviceHandler.addEventListener(
-      DeviceHandler.VOLUME_NAVIGATION_REQUESTED, function(event) {
+      DeviceHandler.VOLUME_NAVIGATION_REQUESTED, event => {
         resolver.resolve(event);
       });
 
@@ -120,7 +120,7 @@
 
   reportPromise(
       resolver.promise.then(
-          function(event) {
+          event => {
             assertEquals('blabbity', event.volumeId);
           }),
       callback);
@@ -143,7 +143,7 @@
   });
 
   reportPromise(
-      mockChrome.notifications.resolver.promise.then(function(notifications) {
+      mockChrome.notifications.resolver.promise.then(notifications => {
         assertEquals(1, Object.keys(notifications).length);
         assertEquals(
             'REMOVABLE_DEVICE_NAVIGATION_MESSAGE',
@@ -184,7 +184,7 @@
   });
 
   reportPromise(
-      mockChrome.notifications.resolver.promise.then(function(notifications) {
+      mockChrome.notifications.resolver.promise.then(notifications => {
         assertFalse(!!notifications['device:/device/path']);
         assertEquals(
             'DEVICE_UNKNOWN: label',
@@ -221,7 +221,7 @@
   });
 
   reportPromise(
-      mockChrome.notifications.resolver.promise.then(function(notifications) {
+      mockChrome.notifications.resolver.promise.then(notifications => {
         assertEquals(1, Object.keys(notifications).length);
         assertEquals(
             'REMOVABLE_DEVICE_NAVIGATION_MESSAGE',
@@ -244,7 +244,7 @@
   });
 
   reportPromise(
-      mockChrome.notifications.resolver.promise.then(function(notifications) {
+      mockChrome.notifications.resolver.promise.then(notifications => {
         assertFalse(!!mockChrome.notifications.items['device:/device/path']);
         assertEquals(
             'DEVICE_UNSUPPORTED: label',
@@ -268,7 +268,7 @@
   });
 
   reportPromise(
-      mockChrome.notifications.resolver.promise.then(function(notifications) {
+      mockChrome.notifications.resolver.promise.then(notifications => {
         assertFalse(!!mockChrome.notifications.items['device:/device/path']);
         const item = mockChrome.notifications.items['deviceFail:/device/path'];
         assertEquals('DEVICE_UNKNOWN_DEFAULT_MESSAGE', item.message);
@@ -293,7 +293,7 @@
   });
 
   reportPromise(
-      mockChrome.notifications.resolver.promise.then(function(notifications) {
+      mockChrome.notifications.resolver.promise.then(notifications => {
         assertFalse(!!mockChrome.notifications.items['device:/device/path']);
         const item = mockChrome.notifications.items['deviceFail:/device/path'];
         assertEquals('DEVICE_UNKNOWN_DEFAULT_MESSAGE', item.message);
@@ -353,13 +353,13 @@
 
   reportPromise(
       mockChrome.notifications.resolver.promise
-          .then(function(notifications) {
+          .then(notifications => {
             assertEquals(1, Object.keys(notifications).length);
             assertEquals(
                 'REMOVABLE_DEVICE_NAVIGATION_MESSAGE',
                 notifications['deviceNavigation:/device/path'].message);
           })
-          .then(function() {
+          .then(() => {
             mockChrome.fileManagerPrivate.onMountCompleted.dispatch({
               eventType: 'mount',
               status: 'error_unsupported_filesystem',
@@ -372,7 +372,7 @@
               shouldNotify: true
             });
           })
-          .then(function() {
+          .then(() => {
             const notifications = mockChrome.notifications.items;
             assertEquals(
                 2, Object.keys(notifications).length);
@@ -397,7 +397,7 @@
   });
 
   reportPromise(
-      mockChrome.notifications.resolver.promise.then(function(notifications) {
+      mockChrome.notifications.resolver.promise.then(notifications => {
         assertEquals(1, Object.keys(notifications).length);
         assertEquals(
             'DEVICE_UNKNOWN: label',
@@ -421,7 +421,7 @@
   });
 
   reportPromise(
-      mockChrome.notifications.resolver.promise.then(function(notifications) {
+      mockChrome.notifications.resolver.promise.then(notifications => {
         assertEquals(1, Object.keys(notifications).length);
         assertEquals(
             'DEVICE_UNKNOWN: \u30E9\u30D9\u30EB',
@@ -585,7 +585,7 @@
   // Add a listener for navigation-requested events.
   const resolver = new importer.Resolver();
   deviceHandler.addEventListener(
-      DeviceHandler.VOLUME_NAVIGATION_REQUESTED, function(event) {
+      DeviceHandler.VOLUME_NAVIGATION_REQUESTED, event => {
         resolver.resolve(event);
       });
 
@@ -594,7 +594,7 @@
   mockChrome.notifications.onClicked.dispatch(notificationId);
   reportPromise(
       resolver.promise.then(
-          function(event) {
+          event => {
             assertEquals(null, event.volumeId);
             assertEquals(devicePath, event.devicePath);
             assertEquals(null, event.filePath);
diff --git a/ui/file_manager/file_manager/background/js/drive_sync_handler.js b/ui/file_manager/file_manager/background/js/drive_sync_handler.js
index 84ab097..56847cb0 100644
--- a/ui/file_manager/file_manager/background/js/drive_sync_handler.js
+++ b/ui/file_manager/file_manager/background/js/drive_sync_handler.js
@@ -112,7 +112,7 @@
  * Returns the completed event name.
  * @return {string}
  */
-DriveSyncHandlerImpl.prototype.getCompletedEventName = function() {
+DriveSyncHandlerImpl.prototype.getCompletedEventName = () => {
   return DriveSyncHandlerImpl.DRIVE_SYNC_COMPLETED_EVENT;
 };
 
@@ -137,7 +137,7 @@
 /**
  * Shows a notification that Drive sync is disabled on cellular networks.
  */
-DriveSyncHandlerImpl.prototype.showDisabledMobileSyncNotification = function() {
+DriveSyncHandlerImpl.prototype.showDisabledMobileSyncNotification = () => {
   chrome.notifications.create(
       DriveSyncHandlerImpl.DISABLED_MOBILE_SYNC_NOTIFICATION_ID_, {
         type: 'basic',
@@ -147,7 +147,7 @@
         buttons:
             [{title: str('DISABLED_MOBILE_SYNC_NOTIFICATION_ENABLE_BUTTON')}]
       },
-      function() {});
+      () => {});
 };
 
 /**
@@ -179,10 +179,10 @@
  * @private
  */
 DriveSyncHandlerImpl.prototype.updateItem_ = function(status) {
-  this.queue_.run(function(callback) {
+  this.queue_.run(callback => {
     window.webkitResolveLocalFileSystemURL(
         status.fileUrl,
-        function(entry) {
+        entry => {
           this.item_.state = ProgressItemState.PROGRESSING;
           this.item_.type = ProgressItemType.SYNC;
           this.item_.quiet = true;
@@ -197,13 +197,13 @@
           this.item_.progressMax = status.total || 0;
           this.progressCenter_.updateItem(this.item_);
           callback();
-        }.bind(this),
-        function(error) {
+        },
+        error => {
           console.warn(
               'Resolving URL ' + status.fileUrl + ' is failed: ', error);
           callback();
         });
-  }.bind(this));
+  });
 };
 
 /**
@@ -212,14 +212,14 @@
  * @private
  */
 DriveSyncHandlerImpl.prototype.removeItem_ = function(status) {
-  this.queue_.run(function(callback) {
+  this.queue_.run(callback => {
     this.item_.state = status.transferState === 'completed' ?
         ProgressItemState.COMPLETED : ProgressItemState.CANCELED;
     this.progressCenter_.updateItem(this.item_);
     this.syncing_ = false;
     this.dispatchEvent(new Event(this.getCompletedEventName()));
     callback();
-  }.bind(this));
+  });
 };
 
 /**
@@ -237,7 +237,7 @@
  * @private
  */
 DriveSyncHandlerImpl.prototype.onDriveSyncError_ = function(event) {
-  window.webkitResolveLocalFileSystemURL(event.fileUrl, function(entry) {
+  window.webkitResolveLocalFileSystemURL(event.fileUrl, entry => {
     const item = new ProgressCenterItem();
     item.type = ProgressItemType.SYNC;
     item.quiet = true;
@@ -266,7 +266,7 @@
           (this.errorIdCounter_++);
     }
     this.progressCenter_.updateItem(item);
-  }.bind(this));
+  });
 };
 
 /**
@@ -275,13 +275,12 @@
  * @param {number} buttonIndex Index of the button.
  * @private
  */
-DriveSyncHandlerImpl.prototype.onNotificationButtonClicked_ = function(
-    notificationId, buttonIndex) {
+DriveSyncHandlerImpl.prototype.onNotificationButtonClicked_ = (notificationId, buttonIndex) => {
   const expectedId = DriveSyncHandlerImpl.DISABLED_MOBILE_SYNC_NOTIFICATION_ID_;
   if (notificationId !== expectedId) {
     return;
   }
-  chrome.notifications.clear(notificationId, function() {});
+  chrome.notifications.clear(notificationId, () => {});
   chrome.fileManagerPrivate.setPreferences({cellularDisabled: false});
 };
 
@@ -290,9 +289,9 @@
  * @private
  */
 DriveSyncHandlerImpl.prototype.onPreferencesChanged_ = function() {
-  chrome.fileManagerPrivate.getPreferences(function(pref) {
+  chrome.fileManagerPrivate.getPreferences(pref => {
     this.cellularDisabled_ = pref.cellularDisabled;
-  }.bind(this));
+  });
 };
 
 /**
diff --git a/ui/file_manager/file_manager/background/js/duplicate_finder.js b/ui/file_manager/file_manager/background/js/duplicate_finder.js
index d79ea56..9cd265b 100644
--- a/ui/file_manager/file_manager/background/js/duplicate_finder.js
+++ b/ui/file_manager/file_manager/background/js/duplicate_finder.js
@@ -35,7 +35,7 @@
            * @param {!Array<string>} urls
            * @return {boolean}
            */
-          function(urls) {
+          urls => {
             return urls.length > 0;
           });
 };
@@ -56,7 +56,7 @@
  * @private
  */
 importer.DriveDuplicateFinder.prototype.computeHash_ = function(entry) {
-  return importer.createMetadataHashcode(entry).then(function(hashcode) {
+  return importer.createMetadataHashcode(entry).then(hashcode => {
     // Cache key is the concatination of metadata hashcode and URL.
     const cacheKey = hashcode + '|' + entry.toURL();
     if (this.hashCache_.hasKey(cacheKey)) {
@@ -64,16 +64,16 @@
     }
 
     const hashPromise = new Promise(
-        (/** @this {importer.DriveDuplicateFinder} */
-        function(resolve, reject) {
+        /** @this {importer.DriveDuplicateFinder} */
+        (resolve, reject) => {
           const startTime = new Date().getTime();
           chrome.fileManagerPrivate.computeChecksum(
               entry,
-              (/**
-               * @param {string|undefined} result The content hash.
-               * @this {importer.DriveDuplicateFinder}
-               */
-              function(result) {
+              /**
+              * @param {string|undefined} result The content hash.
+              * @this {importer.DriveDuplicateFinder}
+              */
+              result => {
                 const elapsedTime = new Date().getTime() - startTime;
                 // Send the timing to GA only if it is sorta exceptionally long.
                 // A one second, CPU intensive operation, is pretty long.
@@ -89,12 +89,12 @@
                 } else {
                   resolve(result);
                 }
-              }).bind(this));
-        }).bind(this));
+              });
+        });
 
     this.hashCache_.put(cacheKey, hashPromise);
     return hashPromise;
-  }.bind(this));
+  });
 };
 
 /**
@@ -122,7 +122,7 @@
              * @param {!VolumeManager} volumeManager
              * @return {string} ID of the user's Drive volume.
              */
-            function(volumeManager) {
+            volumeManager => {
               return volumeManager.getCurrentProfileVolumeInfo(
                   VolumeManagerCommon.VolumeType.DRIVE).volumeId;
             });
@@ -140,17 +140,17 @@
 importer.DriveDuplicateFinder.prototype.searchFilesByHash_ =
     function(hash, volumeId) {
   return new Promise(
-      (/** @this {importer.DriveDuplicateFinder} */
-      function(resolve, reject) {
+      /** @this {importer.DriveDuplicateFinder} */
+      (resolve, reject) => {
         const startTime = new Date().getTime();
         chrome.fileManagerPrivate.searchFilesByHashes(
             volumeId,
             [hash],
-            (/**
-             * @param {!Object<string, !Array<string>>|undefined} urls
-             * @this {importer.DriveDuplicateFinder}
-             */
-            function(urls) {
+            /**
+            * @param {!Object<string, !Array<string>>|undefined} urls
+            * @this {importer.DriveDuplicateFinder}
+            */
+            urls => {
               const elapsedTime = new Date().getTime() - startTime;
               // Send the timing to GA only if it is sorta exceptionally long.
               if (elapsedTime >=
@@ -163,8 +163,8 @@
               } else {
                 resolve(urls[hash]);
               }
-            }).bind(this));
-      }).bind(this));
+            });
+      });
 };
 
 /**
@@ -206,15 +206,15 @@
   }
 
   return new Promise(
-      (/** @this {importer.DispositionChecker} */
-      function(resolve, reject) {
+      /** @this {importer.DispositionChecker} */
+      (resolve, reject) => {
         this.hasHistoryDuplicate_(entry, destination)
             .then(
-                (/**
-                 * @param {boolean} duplicate
-                 * @this {importer.DispositionChecker}
-                 */
-                function(duplicate) {
+                /**
+                * @param {boolean} duplicate
+                * @this {importer.DispositionChecker}
+                */
+                duplicate => {
                   if (duplicate) {
                     resolve(importer.Disposition.HISTORY_DUPLICATE);
                     return;
@@ -226,7 +226,7 @@
                   this.contentMatcher_.isDuplicate(entry)
                       .then(
                           /** @param {boolean} duplicate */
-                          function(duplicate) {
+                          duplicate => {
                             if (duplicate) {
                               resolve(
                                   importer.Disposition.CONTENT_DUPLICATE);
@@ -234,8 +234,8 @@
                               resolve(importer.Disposition.ORIGINAL);
                             }
                           });
-                }).bind(this));
-            }).bind(this));
+                });
+            });
 };
 
 /**
@@ -249,12 +249,11 @@
     function(entry, destination) {
   return this.historyLoader_.getHistory()
       .then(
-          (/**
-           * @param {!importer.ImportHistory} history
-           * @return {!Promise}
-           * @this {importer.DispositionChecker}
-           */
-          function(history) {
+          /**
+          * @param {!importer.ImportHistory} history
+          * @return {!Promise}
+          */
+          history => {
             return Promise.all([
               history.wasCopied(entry, destination),
               history.wasImported(entry, destination)
@@ -263,10 +262,10 @@
                  * @param {!Array<boolean>} results
                  * @return {boolean}
                  */
-                function(results) {
+                results => {
                   return results[0] || results[1];
                 });
-          }).bind(this));
+          });
 };
 
 /**
@@ -276,7 +275,7 @@
  *
  * @return {!importer.DispositionChecker.CheckerFunction}
  */
-importer.DispositionChecker.createChecker = function(historyLoader) {
+importer.DispositionChecker.createChecker = historyLoader => {
   const checker = new importer.DispositionChecker(
       historyLoader, new importer.DriveDuplicateFinder());
   return checker.getDisposition.bind(checker);
diff --git a/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.js b/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.js
index ec0bb83..e0036f2 100644
--- a/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.js
+++ b/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.js
@@ -53,7 +53,7 @@
         const result = {};
         hashes.forEach(
             /** @param {string} hash */
-            function(hash) {
+            hash => {
               result[hash] = fileUrls[hash] || [];
             });
         callback(result);
@@ -88,7 +88,7 @@
   reportPromise(
       duplicateFinder.isDuplicate(files[0])
           .then(
-              function(isDuplicate) {
+              isDuplicate => {
                 assertTrue(isDuplicate);
               }),
       callback);
@@ -108,7 +108,7 @@
   reportPromise(
       duplicateFinder.isDuplicate(newFile)
           .then(
-              function(isDuplicate) {
+              isDuplicate => {
                 assertFalse(isDuplicate);
               }),
       callback);
@@ -123,7 +123,7 @@
       getDisposition(
           files[0], importer.Destination.GOOGLE_DRIVE,
           importer.ScanMode.CONTENT)
-          .then(function(disposition) {
+          .then(disposition => {
             assertEquals(importer.Disposition.CONTENT_DUPLICATE, disposition);
           }),
       callback);
@@ -141,7 +141,7 @@
       getDisposition(
           files[0], importer.Destination.GOOGLE_DRIVE,
           importer.ScanMode.CONTENT)
-          .then(function(disposition) {
+          .then(disposition => {
             assertEquals(importer.Disposition.HISTORY_DUPLICATE, disposition);
           }),
       callback);
@@ -159,7 +159,7 @@
   reportPromise(
       getDisposition(
           newFile, importer.Destination.GOOGLE_DRIVE, importer.ScanMode.CONTENT)
-          .then(function(disposition) {
+          .then(disposition => {
             assertEquals(importer.Disposition.ORIGINAL, disposition);
           }),
       callback);
@@ -175,11 +175,11 @@
   fileSystem.populate(filePaths);
 
   const files = filePaths.map(
-      function(filename) {
+      filename => {
         return fileSystem.entries[filename];
       });
 
-  files.forEach(function(file, index) {
+  files.forEach((file, index) => {
     hashes[file.toURL()] = fileHashes[index];
     fileUrls[fileHashes[index]] = file.toURL();
   });
diff --git a/ui/file_manager/file_manager/background/js/file_operation_handler.js b/ui/file_manager/file_manager/background/js/file_operation_handler.js
index af73ba6..6d078b2 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_handler.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_handler.js
@@ -61,7 +61,7 @@
  * @return {string} message.
  * @private
  */
-FileOperationHandler.getMessage_ = function(event) {
+FileOperationHandler.getMessage_ = event => {
   if (event.reason === fileOperationUtil.EventRouter.EventType.ERROR) {
     switch (event.error.code) {
       case util.FileOperationErrorType.TARGET_EXISTS:
@@ -118,7 +118,7 @@
  * @return {string} message.
  * @private
  */
-FileOperationHandler.getDeleteMessage_ = function(event) {
+FileOperationHandler.getDeleteMessage_ = event => {
   event = /** @type {FileOperationProgressEvent} */ (event);
   if (event.reason === fileOperationUtil.EventRouter.EventType.ERROR) {
     return str('DELETE_ERROR');
@@ -139,7 +139,7 @@
  *     operation type.
  * @private
  */
-FileOperationHandler.getType_ = function(operationType) {
+FileOperationHandler.getType_ = operationType => {
   switch (operationType) {
     case 'COPY': return ProgressItemType.COPY;
     case 'MOVE': return ProgressItemType.MOVE;
diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager.js b/ui/file_manager/file_manager/background/js/file_operation_manager.js
index dad64ca..f1ba4358 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_manager.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_manager.js
@@ -141,34 +141,33 @@
  * @return {Promise} Promise fulfilled with the filtered entry. This is not
  *     rejected.
  */
-FileOperationManagerImpl.prototype.filterSameDirectoryEntry = function(
-    sourceEntries, targetEntry, isMove) {
+FileOperationManagerImpl.prototype.filterSameDirectoryEntry = (sourceEntries, targetEntry, isMove) => {
   if (!isMove) {
     return Promise.resolve(sourceEntries);
   }
   // Utility function to concat arrays.
-  const compactArrays = function(arrays) {
-    return arrays.filter(function(element) {
+  const compactArrays = arrays => {
+    return arrays.filter(element => {
       return !!element;
     });
   };
   // Call processEntry for each item of entries.
-  const processEntries = function(entries) {
+  const processEntries = entries => {
     const promises = entries.map(processFileOrDirectoryEntries);
     return Promise.all(promises).then(compactArrays);
   };
   // Check all file entries and keeps only those need sharing operation.
-  var processFileOrDirectoryEntries = function(entry) {
-    return new Promise(function(resolve) {
+  var processFileOrDirectoryEntries = entry => {
+    return new Promise(resolve => {
       entry.getParent(
-          function(inParentEntry) {
+          inParentEntry => {
             if (!util.isSameEntry(inParentEntry, targetEntry)) {
               resolve(entry);
             } else {
               resolve(null);
             }
           },
-          function(error) {
+          error => {
             console.error(error.stack || error);
             resolve(null);
           });
@@ -197,13 +196,13 @@
   }
 
   this.filterSameDirectoryEntry(sourceEntries, targetEntry, isMove)
-      .then(function(entries) {
+      .then(entries => {
         if (entries.length === 0) {
           return;
         }
         this.queueCopy_(targetEntry, entries, isMove, opt_taskId);
-      }.bind(this))
-      .catch(function(error) {
+      })
+      .catch(error => {
         console.error(error.stack || error);
       });
 };
@@ -245,10 +244,10 @@
       task.getStatus(),
       task.taskId);
 
-  task.initialize(function() {
+  task.initialize(() => {
     this.pendingCopyTasks_.push(task);
     this.serviceAllTasks_();
-  }.bind(this));
+  });
 };
 
 /**
@@ -267,10 +266,10 @@
   }
 
   if (!this.volumeManager_) {
-    volumeManagerFactory.getInstance().then(function(volumeManager) {
+    volumeManagerFactory.getInstance().then(volumeManager => {
       this.volumeManager_ = volumeManager;
       this.serviceAllTasks_();
-    }.bind(this));
+    });
     return;
   }
 
@@ -322,9 +321,9 @@
         task.taskId);
   }.bind(this, nextTask);
 
-  const onEntryChanged = function(kind, entry) {
+  const onEntryChanged = (kind, entry) => {
     this.eventRouter_.sendEntryChangedEvent(kind, entry);
-  }.bind(this);
+  };
 
   // Since getVolumeInfo of targetDirEntry might not be available when this
   // callback is called, bind volume id here.
@@ -384,12 +383,12 @@
   for (let i = 0; i < task.entries.length; i++) {
     group.add(function(entry, callback) {
       metadataProxy.getEntryMetadata(entry).then(
-          function(metadata) {
+          metadata => {
             task.entrySize[entry.toURL()] = metadata.size;
             task.totalBytes += metadata.size;
             callback();
           },
-          function() {
+          () => {
             // Fail to obtain the metadata. Use fake value 1.
             task.entrySize[entry.toURL()] = 1;
             task.totalBytes += 1;
@@ -399,14 +398,14 @@
   }
 
   // Add a delete task.
-  group.run(function() {
+  group.run(() => {
     this.deleteTasks_.push(task);
     this.eventRouter_.sendDeleteEvent(
         fileOperationUtil.EventRouter.EventType.BEGIN, task);
     if (this.deleteTasks_.length === 1) {
       this.serviceAllDeleteTasks_();
     }
-  }.bind(this));
+  });
 };
 
 /**
@@ -420,12 +419,12 @@
 FileOperationManagerImpl.prototype.serviceAllDeleteTasks_ = function() {
   this.serviceDeleteTask_(
       this.deleteTasks_[0],
-      function() {
+      () => {
         this.deleteTasks_.shift();
         if (this.deleteTasks_.length) {
           this.serviceAllDeleteTasks_();
         }
-      }.bind(this));
+      });
 };
 
 /**
@@ -441,7 +440,7 @@
 
   // Delete each entry.
   let error = null;
-  const deleteOneEntry = function(inCallback) {
+  const deleteOneEntry = inCallback => {
     if (!task.entries.length || task.cancelRequested || error) {
       inCallback();
       return;
@@ -450,22 +449,22 @@
         fileOperationUtil.EventRouter.EventType.PROGRESS, task);
     util.removeFileOrDirectory(
         task.entries[0],
-        function() {
+        () => {
           this.eventRouter_.sendEntryChangedEvent(
               util.EntryChangedKind.DELETED, task.entries[0]);
           task.processedBytes += task.entrySize[task.entries[0].toURL()];
           task.entries.shift();
           deleteOneEntry(inCallback);
-        }.bind(this),
-        function(inError) {
+        },
+        inError => {
           error = inError;
           inCallback();
-        }.bind(this));
-  }.bind(this);
+        });
+  };
   queue.run(deleteOneEntry);
 
   // Send an event and finish the async steps.
-  queue.run(function(inCallback) {
+  queue.run(inCallback => {
     const EventType = fileOperationUtil.EventRouter.EventType;
     let reason;
     if (error) {
@@ -478,7 +477,7 @@
     this.eventRouter_.sendDeleteEvent(reason, task);
     inCallback();
     callback();
-  }.bind(this));
+  });
 };
 
 /**
@@ -495,10 +494,10 @@
       fileOperationUtil.EventRouter.EventType.BEGIN,
       zipTask.getStatus(),
       zipTask.taskId);
-  zipTask.initialize(function() {
+  zipTask.initialize(() => {
     this.pendingCopyTasks_.push(zipTask);
     this.serviceAllTasks_();
-  }.bind(this));
+  });
 };
 
 /**
diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js b/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js
index 33e15e5..84b4c02 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js
@@ -98,7 +98,7 @@
  */
 BlockableFakeStartCopy.prototype.startCopyFunc = function(
     source, destination, newName, callback) {
-  const makeStatus = function(type) {
+  const makeStatus = type => {
     return {
       type: type,
       sourceUrl: source.toURL(),
@@ -106,7 +106,7 @@
     };
   };
 
-  const completeCopyOperation = function(copyId) {
+  const completeCopyOperation = copyId => {
     const newPath = joinPath('/', newName);
     const fileSystem = getFileSystemForURL(
         this.fileSystems_, destination.toURL());
@@ -115,7 +115,7 @@
         /** @type {!MockEntry} */ (mockEntry.clone(newPath));
     listener(copyId, makeStatus('end_copy_entry'));
     listener(copyId, makeStatus('success'));
-  }.bind(this);
+  };
 
   this.startCopyId_++;
 
@@ -218,20 +218,20 @@
  * @return {Promise} Promise to be fulfilled with an event list.
  */
 function waitForEvents(fileOperationManager) {
-  return new Promise(function(fulfill) {
+  return new Promise(fulfill => {
     const events = [];
-    fileOperationManager.addEventListener('copy-progress', function(event) {
+    fileOperationManager.addEventListener('copy-progress', event => {
       event = /** @type {FileOperationProgressEvent} */ (event);
       events.push(event);
       if (event.reason === 'SUCCESS') {
         fulfill(events);
       }
     });
-    fileOperationManager.addEventListener('entries-changed', function(event) {
+    fileOperationManager.addEventListener('entries-changed', event => {
       event = /** @type {FileOperationProgressEvent} */ (event);
       events.push(event);
     });
-    fileOperationManager.addEventListener('delete', function(event) {
+    fileOperationManager.addEventListener('delete', event => {
       event = /** @type {FileOperationProgressEvent} */ (event);
       events.push(event);
       if (event.reason === 'SUCCESS') {
@@ -254,7 +254,7 @@
  * volume manager instance.
  * @return {Promise}
  */
-volumeManagerFactory.getInstance = function() {
+volumeManagerFactory.getInstance = () => {
   return Promise.resolve(volumeManager);
 };
 
@@ -289,10 +289,10 @@
   const errorPromise =
       fileOperationUtil.resolvePath(root, '/not_found')
           .then(
-              function() {
+              () => {
                 assertTrue(false, 'The NOT_FOUND error is not reported.');
               },
-              function(error) {
+              error => {
                 return error.name;
               });
   reportPromise(Promise.all([
@@ -300,7 +300,7 @@
     filePromise,
     directoryPromise,
     errorPromise
-  ]).then(function(results) {
+  ]).then(results => {
     assertArrayEquals([
       fileSystem.entries['/'],
       fileSystem.entries['/file'],
@@ -333,14 +333,14 @@
   fileOperationUtil
       .findEntriesRecursively(
           fileSystem.root,
-          function(fileEntry) {
+          fileEntry => {
             foundFiles.push(fileEntry);
           })
-      .then(function() {
+      .then(() => {
         assertEquals(12, foundFiles.length);
         callback(false);
       })
-      .catch(function() {
+      .catch(() => {
         const error = true;
         callback(error);
       });
@@ -369,19 +369,19 @@
   fileOperationUtil
       .findFilesRecursively(
           fileSystem.root,
-          function(fileEntry) {
+          fileEntry => {
             foundFiles.push(fileEntry);
           })
       .then(
-          function() {
+          () => {
             assertEquals(10, foundFiles.length);
             foundFiles.forEach(
-                function(entry) {
+                entry => {
                   assertTrue(entry.isFile);
                 });
             callback(false);
           })
-      .catch(function() {
+      .catch(() => {
         const error = true;
         callback(error);
       });
@@ -407,11 +407,11 @@
   });
 
   fileOperationUtil.gatherEntriesRecursively(fileSystem.root)
-      .then(function(gatheredFiles) {
+      .then(gatheredFiles => {
         assertEquals(12, gatheredFiles.length);
         callback(false);
       })
-      .catch(function() {
+      .catch(() => {
         const error = true;
         callback(error);
       });
@@ -443,17 +443,17 @@
 
   const nonExistingPromise =
       fileOperationUtil.deduplicatePath(fileSystem1.root, 'file.txt').
-      then(function(path) {
+      then(path => {
         assertEquals('file.txt', path);
       });
   const existingPathPromise =
       fileOperationUtil.deduplicatePath(fileSystem2.root, 'file.txt').
-      then(function(path) {
+      then(path => {
         assertEquals('file (1).txt', path);
       });
   const moreExistingPathPromise =
       fileOperationUtil.deduplicatePath(fileSystem3.root, 'file.txt').
-      then(function(path) {
+      then(path => {
         assertEquals('file (10).txt', path);
       });
 
@@ -475,13 +475,12 @@
     '/': DIRECTORY_SIZE,
     '/test.txt': 10,
   });
-  window.webkitResolveLocalFileSystemURL = function(url, success, failure) {
+  window.webkitResolveLocalFileSystemURL = (url, success, failure) => {
     resolveTestFileSystemURL(fileSystem, url, success, failure);
   };
 
-  mockChrome.fileManagerPrivate.startCopy = function(
-      source, destination, newName, callback) {
-    const makeStatus = function(type) {
+  mockChrome.fileManagerPrivate.startCopy = (source, destination, newName, callback) => {
+    const makeStatus = type => {
       return {
         type: type,
         sourceUrl: source.toURL(),
@@ -508,7 +507,7 @@
   const eventsPromise = waitForEvents(fileOperationManager);
 
   // Verify the events.
-  reportPromise(eventsPromise.then(function(events) {
+  reportPromise(eventsPromise.then(events => {
     const firstEvent = events[0];
     assertEquals('BEGIN', firstEvent.reason);
     assertEquals(1, firstEvent.status.numRemainingItems);
@@ -521,13 +520,13 @@
     assertEquals(10, lastEvent.status.processedBytes);
     assertEquals(10, lastEvent.status.totalBytes);
 
-    assertTrue(events.some(function(event) {
+    assertTrue(events.some(event => {
       return event.type === 'entries-changed' &&
           event.kind === util.EntryChangedKind.CREATED &&
           event.entries[0].fullPath === '/test (1).txt';
     }));
 
-    assertFalse(events.some(function(event) {
+    assertFalse(events.some(event => {
       return event.type === 'delete';
     }));
   }), callback);
@@ -548,7 +547,7 @@
     '/dest': DIRECTORY_SIZE,
     '/test.txt': 10
   });
-  window.webkitResolveLocalFileSystemURL = function(url, success, failure) {
+  window.webkitResolveLocalFileSystemURL = (url, success, failure) => {
     resolveTestFileSystemURL(fileSystem, url, success, failure);
   };
 
@@ -570,10 +569,10 @@
       /** @type {!DirectoryEntry} */ (fileSystem.entries['/dest']), false);
 
   let firstOperationTaskId;
-  reportPromise(waitUntil(function() {
+  reportPromise(waitUntil(() => {
     // Wait until the first operation is blocked.
     return blockableFakeStartCopy.resolveBlockedOperationCallback !== null;
-  }).then(function() {
+  }).then(() => {
     assertEquals(1, eventLogger.events.length);
     assertEquals('BEGIN', eventLogger.events[0].reason);
     firstOperationTaskId = eventLogger.events[0].taskId;
@@ -583,10 +582,10 @@
         [fileSystem.entries['/test.txt']],
         /** @type {!DirectoryEntry} */ (fileSystem.entries['/']), false);
 
-    return waitUntil(function() {
+    return waitUntil(() => {
       return fileOperationManager.getPendingCopyTasksForTesting().length === 1;
     });
-  }).then(function() {
+  }).then(() => {
     // Asserts that the second operation is added to pending copy tasks. Current
     // implementation run tasks synchronusly after adding it to pending tasks.
     // TODO(yawano) This check deeply depends on the implementation. Find a
@@ -596,10 +595,10 @@
 
     blockableFakeStartCopy.resolveBlockedOperationCallback();
 
-    return waitUntil(function() {
+    return waitUntil(() => {
       return eventLogger.numberOfSuccessEvents === 2;
     });
-  }).then(function() {
+  }).then(() => {
     // Events should be the following.
     // BEGIN: first operation
     // BEGIN: second operation
@@ -633,7 +632,7 @@
   });
   const fileSystems = [fileSystemA, fileSystemB];
 
-  window.webkitResolveLocalFileSystemURL = function(url, success, failure) {
+  window.webkitResolveLocalFileSystemURL = (url, success, failure) => {
     const system = getFileSystemForURL(fileSystems, url);
     resolveTestFileSystemURL(system, url, success, failure);
   };
@@ -657,9 +656,9 @@
       /** @type {!DirectoryEntry} */ (fileSystemB.entries['/']), false);
 
   let firstOperationTaskId;
-  reportPromise(waitUntil(function() {
+  reportPromise(waitUntil(() => {
     return blockableFakeStartCopy.resolveBlockedOperationCallback !== null;
-  }).then(function() {
+  }).then(() => {
     assertEquals(1, eventLogger.events.length);
     assertEquals('BEGIN', eventLogger.events[0].reason);
     firstOperationTaskId = eventLogger.events[0].taskId;
@@ -671,18 +670,18 @@
         /** @type {!DirectoryEntry} */ (fileSystemA.entries['/']), false);
 
     // Wait until the second operation is completed.
-    return waitUntil(function() {
+    return waitUntil(() => {
       return eventLogger.numberOfSuccessEvents === 1;
     });
-  }).then(function() {
+  }).then(() => {
     // Resolve the blocked operation.
     blockableFakeStartCopy.resolveBlockedOperationCallback();
 
     // Wait until the blocked operation is completed.
-    return waitUntil(function() {
+    return waitUntil(() => {
       return eventLogger.numberOfSuccessEvents === 2;
     });
-  }).then(function() {
+  }).then(() => {
     // Events should be following.
     // BEGIN: first operation
     // BEGIN: second operation
@@ -729,9 +728,9 @@
       [fileSystem.entries['/test.txt']],
       /** @type {!DirectoryEntry} */ (fileSystem.entries['/']), false);
 
-  reportPromise(waitUntil(function() {
+  reportPromise(waitUntil(() => {
     return eventLogger.numberOfErrorEvents === 1;
-  }).then(function() {
+  }).then(() => {
     // Since the task fails with an error, pending copy tasks should be empty.
     assertEquals(0,
         fileOperationManager.getPendingCopyTasksForTesting().length);
@@ -756,7 +755,7 @@
     '/directory': DIRECTORY_SIZE,
     '/test.txt': 10,
   });
-  window.webkitResolveLocalFileSystemURL = function(url, success, failure) {
+  window.webkitResolveLocalFileSystemURL = (url, success, failure) => {
     resolveTestFileSystemURL(fileSystem, url, success, failure);
   };
 
@@ -767,7 +766,7 @@
   const eventsPromise = waitForEvents(fileOperationManager);
 
   // Verify the events.
-  reportPromise(eventsPromise.then(function(events) {
+  reportPromise(eventsPromise.then(events => {
     const firstEvent = events[0];
     assertEquals('BEGIN', firstEvent.reason);
     assertEquals(1, firstEvent.status.numRemainingItems);
@@ -780,19 +779,19 @@
     assertEquals(1, lastEvent.status.processedBytes);
     assertEquals(1, lastEvent.status.totalBytes);
 
-    assertTrue(events.some(function(event) {
+    assertTrue(events.some(event => {
       return event.type === 'entries-changed' &&
           event.kind === util.EntryChangedKind.DELETED &&
           event.entries[0].fullPath === '/test.txt';
     }));
 
-    assertTrue(events.some(function(event) {
+    assertTrue(events.some(event => {
       return event.type === 'entries-changed' &&
           event.kind === util.EntryChangedKind.CREATED &&
           event.entries[0].fullPath === '/directory/test.txt';
     }));
 
-    assertFalse(events.some(function(event) {
+    assertFalse(events.some(event => {
       return event.type === 'delete';
     }));
   }), callback);
@@ -812,13 +811,13 @@
     '/': DIRECTORY_SIZE,
     '/test.txt': 10,
   });
-  window.webkitResolveLocalFileSystemURL = function(url, success, failure) {
+  window.webkitResolveLocalFileSystemURL = (url, success, failure) => {
     resolveTestFileSystemURL(fileSystem, url, success, failure);
   };
 
   // Observing manager's events.
   reportPromise(
-      waitForEvents(fileOperationManager).then(function(events) {
+      waitForEvents(fileOperationManager).then(events => {
         assertEquals('delete', events[0].type);
         assertEquals('BEGIN', events[0].reason);
         assertEquals(10, events[0].totalBytes);
@@ -830,7 +829,7 @@
         assertEquals(10, lastEvent.totalBytes);
         assertEquals(10, lastEvent.processedBytes);
 
-        assertFalse(events.some(function(event) {
+        assertFalse(events.some(event => {
           return event.type === 'copy-progress';
         }));
       }),
@@ -866,7 +865,7 @@
   fileOperationManager = new FileOperationManagerImpl();
 
   // Observing manager's events.
-  reportPromise(waitForEvents(fileOperationManager).then(function(events) {
+  reportPromise(waitForEvents(fileOperationManager).then(events => {
     assertEquals('copy-progress', events[0].type);
     assertEquals('BEGIN', events[0].reason);
     assertEquals(1, events[0].status.totalBytes);
@@ -878,11 +877,11 @@
     assertEquals(10, lastEvent.status.totalBytes);
     assertEquals(10, lastEvent.status.processedBytes);
 
-    assertFalse(events.some(function(event) {
+    assertFalse(events.some(event => {
       return event.type === 'delete';
     }));
 
-    assertTrue(events.some(function(event) {
+    assertTrue(events.some(event => {
       return event.type === 'entries-changed' &&
           event.entries[0].fullPath === '/test.zip';
     }));
diff --git a/ui/file_manager/file_manager/background/js/file_operation_util.js b/ui/file_manager/file_manager/background/js/file_operation_util.js
index 6726b66a..211a0fd 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_util.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_util.js
@@ -16,12 +16,12 @@
  * @return {Promise} Promise fulfilled with the resolved entry, or rejected with
  *     FileError.
  */
-fileOperationUtil.resolvePath = function(root, path) {
+fileOperationUtil.resolvePath = (root, path) => {
   if (path === '' || path === '/') {
     return Promise.resolve(root);
   }
   return new Promise(root.getFile.bind(root, path, {create: false})).
-      catch(function(error) {
+      catch(error => {
         if (error.name === util.FileError.TYPE_MISMATCH_ERR) {
           // Bah.  It's a directory, ask again.
           return new Promise(
@@ -48,8 +48,7 @@
  *     on error.
  * @return {Promise} Promise fulfilled with available path.
  */
-fileOperationUtil.deduplicatePath = function(
-    dirEntry, relativePath, opt_successCallback, opt_errorCallback) {
+fileOperationUtil.deduplicatePath = (dirEntry, relativePath, opt_successCallback, opt_errorCallback) => {
   // Crack the path into three part. The parenthesized number (if exists) will
   // be replaced by incremented number for retry. For example, suppose
   // |relativePath| is "file (10).txt", the second check path will be
@@ -59,11 +58,11 @@
   const ext = match[3] || '';
 
   // Check to see if the target exists.
-  const resolvePath = function(trialPath, copyNumber) {
-    return fileOperationUtil.resolvePath(dirEntry, trialPath).then(function() {
+  const resolvePath = (trialPath, copyNumber) => {
+    return fileOperationUtil.resolvePath(dirEntry, trialPath).then(() => {
       const newTrialPath = prefix + ' (' + copyNumber + ')' + ext;
       return resolvePath(newTrialPath, copyNumber + 1);
-    }, function(error) {
+    }, error => {
       // We expect to be unable to resolve the target file, since we're
       // going to create it during the copy.  However, if the resolve fails
       // with anything other than NOT_FOUND, that's trouble.
@@ -75,7 +74,7 @@
     });
   };
 
-  const promise = resolvePath(relativePath, 1).catch(function(error) {
+  const promise = resolvePath(relativePath, 1).catch(error => {
     if (error instanceof Error) {
       return Promise.reject(error);
     }
@@ -100,13 +99,12 @@
  *     occurred error (i.e. following errors will just be discarded).
  * @private
  */
-fileOperationUtil.resolveRecursively_ = function(
-    entry, successCallback, errorCallback) {
+fileOperationUtil.resolveRecursively_ = (entry, successCallback, errorCallback) => {
   const result = [];
   let error = null;
   let numRunningTasks = 0;
 
-  const maybeInvokeCallback = function() {
+  const maybeInvokeCallback = () => {
     // If there still remain some running tasks, wait their finishing.
     if (numRunningTasks > 0) {
       return;
@@ -120,7 +118,7 @@
   };
 
   // The error handling can be shared.
-  const onError = function(fileError) {
+  const onError = fileError => {
     // If this is the first error, remember it.
     if (!error) {
       error = fileError;
@@ -129,7 +127,7 @@
     maybeInvokeCallback();
   };
 
-  const process = function(entry) {
+  const process = entry => {
     numRunningTasks++;
     result.push(entry);
     if (entry.isDirectory) {
@@ -160,7 +158,7 @@
           onError);
     } else {
       // For a file, annotate the file size.
-      metadataProxy.getEntryMetadata(entry).then(function(metadata) {
+      metadataProxy.getEntryMetadata(entry).then(metadata => {
         entry.size = metadata.size;
         --numRunningTasks;
         maybeInvokeCallback();
@@ -180,18 +178,18 @@
  * @param {!DirectoryEntry} entry The DirectoryEntry to scan.
  * @return {!Promise<!Array<!Entry>>} Resolves when scanning is complete.
  */
-fileOperationUtil.gatherEntriesRecursively = function(entry) {
+fileOperationUtil.gatherEntriesRecursively = entry => {
   /** @type {!Array<!Entry>} */
   const gatheredFiles = [];
 
   return fileOperationUtil.findEntriesRecursively(
       entry,
       /** @param {!Entry} entry */
-      function(entry) {
+      entry => {
         gatheredFiles.push(entry);
       })
       .then(
-          function() {
+          () => {
             return gatheredFiles;
           });
 };
@@ -208,11 +206,11 @@
  *     a {@code FileEntry} is discovered.
  * @return {!Promise} Resolves when scanning is complete.
  */
-fileOperationUtil.findFilesRecursively = function(entry, onResultCallback) {
+fileOperationUtil.findFilesRecursively = (entry, onResultCallback) => {
   return fileOperationUtil.findEntriesRecursively(
       entry,
       /** @param {!Entry} entry */
-      function(entry) {
+      entry => {
         if (entry.isFile) {
           onResultCallback(/** @type {!FileEntry} */ (entry));
         }
@@ -231,9 +229,9 @@
  *     an {@code Entry} is discovered.
  * @return {!Promise} Resolves when scanning is complete.
  */
-fileOperationUtil.findEntriesRecursively = function(entry, onResultCallback) {
+fileOperationUtil.findEntriesRecursively = (entry, onResultCallback) => {
   return new Promise(
-      function(resolve, reject) {
+      (resolve, reject) => {
         let numRunningTasks = 0;
         let scanError = null;
 
@@ -241,7 +239,7 @@
          * @param  {*=} opt_error If defined immediately
          *     terminates scanning.
          */
-        const maybeSettlePromise = function(opt_error) {
+        const maybeSettlePromise = opt_error => {
           scanError = opt_error;
 
           if (scanError) {
@@ -258,7 +256,7 @@
         };
 
         /** @param {!Entry} entry */
-        const processEntry = function(entry) {
+        const processEntry = entry => {
           // All scanning stops when an error is encountered.
           if (scanError) {
             return;
@@ -271,7 +269,7 @@
         };
 
         /** @param {!DirectoryEntry} directory */
-        var processDirectory = function(directory) {
+        var processDirectory = directory => {
           // All scanning stops when an error is encountered.
           if (scanError) {
             return;
@@ -312,15 +310,15 @@
  * @param {function(!Entry)} callback
  * @return {!Promise} Resolves when listing is complete.
  */
-fileOperationUtil.listEntries = function(directory, callback) {
+fileOperationUtil.listEntries = (directory, callback) => {
   return new Promise(
-      function(resolve, reject) {
+      (resolve, reject) => {
         const reader = directory.createReader();
 
-        const readEntries = function() {
+        const readEntries = () => {
           reader.readEntries (
               /** @param {!Array<!Entry>} entries */
-              function(entries) {
+              entries => {
                 if (entries.length === 0) {
                   resolve(undefined);
                   return;
@@ -358,9 +356,15 @@
  *     When the cancel is done, errorCallback will be called. The returned
  *     callback must not be called more than once.
  */
-fileOperationUtil.copyTo = function(
-    source, parent, newName, entryChangedCallback, progressCallback,
-    successCallback, errorCallback) {
+fileOperationUtil.copyTo = (
+  source,
+  parent,
+  newName,
+  entryChangedCallback,
+  progressCallback,
+  successCallback,
+  errorCallback
+) => {
 
   /** @type {number|undefined} */
   let copyId;
@@ -369,8 +373,8 @@
   // Makes the callback called in order they were invoked.
   const callbackQueue = new AsyncUtil.Queue();
 
-  const onCopyProgress = function(progressCopyId, status) {
-    callbackQueue.run(function(callback) {
+  const onCopyProgress = (progressCopyId, status) => {
+    callbackQueue.run(callback => {
       if (copyId === null) {
         // If the copyId is not yet available, wait for it.
         pendingCallbacks.push(
@@ -396,11 +400,11 @@
               parent,
               newName,
               null,
-              function(entry) {
+              entry => {
                 entryChangedCallback(status.sourceUrl, entry);
                 callback();
               },
-              function() {
+              () => {
                 entryChangedCallback(status.sourceUrl, null);
                 callback();
               });
@@ -416,7 +420,7 @@
               onCopyProgress);
           // TODO(mtomasz): Convert URL to Entry in custom bindings.
           util.URLsToEntries(
-              [status.destinationUrl], function(destinationEntries) {
+              [status.destinationUrl], destinationEntries => {
                 successCallback(destinationEntries[0] || null);
                 callback();
               });
@@ -452,7 +456,7 @@
 
   // Then starts the copy.
   chrome.fileManagerPrivate.startCopy(
-      source, parent, newName, function(startCopyId) {
+      source, parent, newName, startCopyId => {
         // last error contains the FileError code on error.
         if (chrome.runtime.lastError) {
           // Unsubscribe the progress listener.
@@ -469,10 +473,10 @@
         }
       });
 
-  return function() {
+  return () => {
     // If copyId is not yet available, wait for it.
     if (copyId === undefined) {
-      pendingCallbacks.push(function() {
+      pendingCallbacks.push(() => {
         chrome.fileManagerPrivate.cancelCopy(
             assert(copyId), util.checkAPIError);
       });
@@ -495,10 +499,9 @@
  * @param {function(DOMError)} errorCallback Callback invoked when an error
  *     is found.
  */
-fileOperationUtil.zipSelection = function(
-    sources, parent, newName, successCallback, errorCallback) {
+fileOperationUtil.zipSelection = (sources, parent, newName, successCallback, errorCallback) => {
   chrome.fileManagerPrivate.zipSelection(
-      sources, parent, newName, function(success) {
+      sources, parent, newName, success => {
         if (!success) {
           // Failed to create a zip archive.
           errorCallback(
@@ -595,7 +598,7 @@
 /**
  * @param {function()} callback When entries resolved.
  */
-fileOperationUtil.Task.prototype.initialize = function(callback) {
+fileOperationUtil.Task.prototype.initialize = callback => {
 };
 
 /**
@@ -622,8 +625,7 @@
  * @param {function(fileOperationUtil.Error)} errorCallback Callback run on
  *     error.
  */
-fileOperationUtil.Task.prototype.run = function(
-    entryChangedCallback, progressCallback, successCallback, errorCallback) {
+fileOperationUtil.Task.prototype.run = (entryChangedCallback, progressCallback, successCallback, errorCallback) => {
 };
 
 /**
@@ -750,7 +752,7 @@
     group.add(function(index, callback) {
       fileOperationUtil.resolveRecursively_(
           this.sourceEntries[index],
-          function(resolvedEntries) {
+          resolvedEntries => {
             const resolvedEntryMap = {};
             for (let j = 0; j < resolvedEntries.length; ++j) {
               const entry = resolvedEntries[j];
@@ -759,8 +761,8 @@
             }
             this.processingEntries[index] = resolvedEntryMap;
             callback();
-          }.bind(this),
-          function(error) {
+          },
+          error => {
             console.error(
                 'Failed to resolve for copy: %s', error.name);
             callback();
@@ -768,7 +770,7 @@
     }.bind(this, i));
   }
 
-  group.run(function() {
+  group.run(() => {
     // Fill totalBytes.
     this.totalBytes = 0;
     for (let i = 0; i < this.processingEntries.length; i++) {
@@ -778,7 +780,7 @@
     }
 
     callback();
-  }.bind(this));
+  });
 };
 
 /**
@@ -805,10 +807,10 @@
 
   // TODO(hidehiko): Delete after copy is the implementation of Move.
   // Migrate the part into MoveTask.run().
-  const deleteOriginals = function() {
+  const deleteOriginals = () => {
     let count = this.sourceEntries.length;
 
-    const onEntryDeleted = function(entry) {
+    const onEntryDeleted = entry => {
       entryChangedCallback(util.EntryChangedKind.DELETED, entry);
       count--;
       if (!count) {
@@ -816,7 +818,7 @@
       }
     };
 
-    const onFilesystemError = function(err) {
+    const onFilesystemError = err => {
       errorCallback(new fileOperationUtil.Error(
           util.FileOperationErrorType.FILESYSTEM_ERROR, err));
     };
@@ -826,7 +828,7 @@
       util.removeFileOrDirectory(
           entry, onEntryDeleted.bind(null, entry), onFilesystemError);
     }
-  }.bind(this);
+  };
 
   /**
    * Accumulates processed bytes and call |progressCallback| if needed.
@@ -881,7 +883,7 @@
 
   AsyncUtil.forEach(
       this.sourceEntries,
-      function(callback, entry, index) {
+      (callback, entry, index) => {
         if (this.cancelRequested_) {
           errorCallback(new fileOperationUtil.Error(
               util.FileOperationErrorType.FILESYSTEM_ERROR,
@@ -890,8 +892,8 @@
         }
         progressCallback();
         this.processEntry_(
-            entry, this.targetDirEntry,
-            function(sourceEntryUrl, destinationEntry) {
+            assert(entry), assert(this.targetDirEntry),
+            (sourceEntryUrl, destinationEntry) => {
               updateProgress(index, sourceEntryUrl);
               // The destination entry may be null, if the copied file got
               // deleted just after copying.
@@ -900,10 +902,10 @@
                     util.EntryChangedKind.CREATED, destinationEntry);
               }
             },
-            function(sourceEntryUrl, size) {
+            (sourceEntryUrl, size) => {
               updateProgress(index, sourceEntryUrl, size);
             },
-            function() {
+            () => {
               // Finishes off delayed updates if necessary.
               this.updateProgressRateLimiter_.runImmediately();
               // Update current source index and processing bytes.
@@ -912,8 +914,8 @@
               this.numRemainingItems = this.calcNumRemainingItems_();
               errorCount = 0;
               callback();
-            }.bind(this),
-            function(error) {
+            },
+            error => {
               // Finishes off delayed updates if necessary.
               this.updateProgressRateLimiter_.runImmediately();
               // Update current source index and processing bytes.
@@ -928,9 +930,9 @@
               } else {
                 errorCallback(error);
               }
-            }.bind(this));
+            });
       },
-      function() {
+      () => {
         if (lastError) {
           errorCallback(lastError);
         } else if (this.deleteAfterCopy) {
@@ -938,8 +940,7 @@
         } else {
           successCallback();
         }
-      }.bind(this),
-      this);
+      });
 };
 
 /**
@@ -962,7 +963,7 @@
     successCallback, errorCallback) {
   fileOperationUtil.deduplicatePath(
       destinationEntry, sourceEntry.name,
-      function(destinationName) {
+      destinationName => {
         if (this.cancelRequested_) {
           errorCallback(new fileOperationUtil.Error(
               util.FileOperationErrorType.FILESYSTEM_ERROR,
@@ -972,16 +973,16 @@
         this.cancelCallback_ = fileOperationUtil.copyTo(
             sourceEntry, destinationEntry, destinationName,
             entryChangedCallback, progressCallback,
-            function(entry) {
+            entry => {
               this.cancelCallback_ = null;
               successCallback();
-            }.bind(this),
-            function(error) {
+            },
+            error => {
               this.cancelCallback_ = null;
               errorCallback(new fileOperationUtil.Error(
                   util.FileOperationErrorType.FILESYSTEM_ERROR, error));
-            }.bind(this));
-      }.bind(this),
+            });
+      },
       errorCallback);
 };
 
@@ -1016,7 +1017,7 @@
   // process the deepest entry first. Since move of each entry is
   // done by a single moveTo() call, we don't need to care about the
   // recursive traversal order.
-  this.sourceEntries.sort(function(entry1, entry2) {
+  this.sourceEntries.sort((entry1, entry2) => {
     return entry2.toURL().length - entry1.toURL().length;
   });
 
@@ -1057,7 +1058,7 @@
 
   AsyncUtil.forEach(
       this.sourceEntries,
-      function(callback, entry, index) {
+      (callback, entry, index) => {
         if (this.cancelRequested_) {
           errorCallback(new fileOperationUtil.Error(
               util.FileOperationErrorType.FILESYSTEM_ERROR,
@@ -1066,20 +1067,19 @@
         }
         progressCallback();
         fileOperationUtil.MoveTask.processEntry_(
-            entry, this.targetDirEntry, entryChangedCallback,
-            function() {
+            assert(entry), assert(this.targetDirEntry), entryChangedCallback,
+            () => {
               // Update current source index.
               this.processingSourceIndex_ = index + 1;
               this.processedBytes = this.calcProcessedBytes_();
               this.numRemainingItems = this.calcNumRemainingItems_();
               callback();
-            }.bind(this),
+            },
             errorCallback);
       },
-      function() {
+      () => {
         successCallback();
-      }.bind(this),
-      this);
+      });
 };
 
 /**
@@ -1094,22 +1094,26 @@
  * @param {function(fileOperationUtil.Error)} errorCallback On error.
  * @private
  */
-fileOperationUtil.MoveTask.processEntry_ = function(
-    sourceEntry, destinationEntry, entryChangedCallback, successCallback,
-    errorCallback) {
+fileOperationUtil.MoveTask.processEntry_ = (
+  sourceEntry,
+  destinationEntry,
+  entryChangedCallback,
+  successCallback,
+  errorCallback
+) => {
   const destination =
       /** @type{!DirectoryEntry} */ (
           assert(util.unwrapEntry(destinationEntry)));
   fileOperationUtil.deduplicatePath(
-      destination, sourceEntry.name, function(destinationName) {
+      destination, sourceEntry.name, destinationName => {
         sourceEntry.moveTo(
             destination, destinationName,
-            function(movedEntry) {
+            movedEntry => {
               entryChangedCallback(util.EntryChangedKind.CREATED, movedEntry);
               entryChangedCallback(util.EntryChangedKind.DELETED, sourceEntry);
               successCallback();
             },
-            function(error) {
+            error => {
               errorCallback(new fileOperationUtil.Error(
                   util.FileOperationErrorType.FILESYSTEM_ERROR, error));
             });
@@ -1155,7 +1159,7 @@
   for (let i = 0; i < this.sourceEntries.length; i++) {
     group.add(function(index, callback) {
       fileOperationUtil.resolveRecursively_(
-          this.sourceEntries[index], function(entries) {
+          this.sourceEntries[index], entries => {
             for (let j = 0; j < entries.length; j++) {
               resolvedEntryMap[entries[j].toURL()] = entries[j];
             }
@@ -1164,7 +1168,7 @@
     }.bind(this, i));
   }
 
-  group.run(function() {
+  group.run(() => {
     // For zip archiving, all the entries are processed at once.
     this.processingEntries = [resolvedEntryMap];
 
@@ -1174,7 +1178,7 @@
     }
 
     callback();
-  }.bind(this));
+  });
 };
 
 /**
@@ -1200,7 +1204,7 @@
 
   fileOperationUtil.deduplicatePath(
       this.targetDirEntry, destName + '.zip',
-      function(destPath) {
+      destPath => {
         // TODO: per-entry zip progress update with accurate byte count.
         // For now just set completedBytes to 0 so that it is not full until
         // the zip operatoin is done.
@@ -1218,16 +1222,16 @@
             entries,
             this.zipBaseDirEntry,
             destPath,
-            function(entry) {
+            entry => {
               this.processedBytes = this.totalBytes;
               entryChangedCallback(util.EntryChangedKind.CREATED, entry);
               successCallback();
-            }.bind(this),
-            function(error) {
+            },
+            error => {
               errorCallback(new fileOperationUtil.Error(
                   util.FileOperationErrorType.FILESYSTEM_ERROR, error));
             });
-      }.bind(this),
+      },
       errorCallback);
 };
 
diff --git a/ui/file_manager/file_manager/background/js/import_history.js b/ui/file_manager/file_manager/background/js/import_history.js
index b5f2f8a..64d42323 100644
--- a/ui/file_manager/file_manager/background/js/import_history.js
+++ b/ui/file_manager/file_manager/background/js/import_history.js
@@ -53,37 +53,37 @@
 
 /** @override */
 importer.DummyImportHistory.prototype.markCopied =
-    function(entry, destination, destinationUrl) {
+    (entry, destination, destinationUrl) => {
   return Promise.resolve();
 };
 
 /** @override */
 importer.DummyImportHistory.prototype.listUnimportedUrls =
-    function(destination) {
+    destination => {
   return Promise.resolve([]);
 };
 
 /** @override */
 importer.DummyImportHistory.prototype.markImported =
-    function(entry, destination) {
+    (entry, destination) => {
   return Promise.resolve();
 };
 
 /** @override */
 importer.DummyImportHistory.prototype.markImportedByUrl =
-    function(destinationUrl) {
+    destinationUrl => {
   return Promise.resolve();
 };
 
 /** @override */
 importer.DummyImportHistory.prototype.addHistoryLoadedListener =
-    function(listener) {};
+    listener => {};
 
 /** @override */
-importer.DummyImportHistory.prototype.addObserver = function(observer) {};
+importer.DummyImportHistory.prototype.addObserver = observer => {};
 
 /** @override */
-importer.DummyImportHistory.prototype.removeObserver = function(observer) {};
+importer.DummyImportHistory.prototype.removeObserver = observer => {};
 
 /**
  * @private @enum {number}
@@ -160,13 +160,13 @@
 importer.PersistentImportHistory.prototype.load_ = function() {
   return this.storage_.readAll(this.updateInMemoryRecord_.bind(this))
       .then(
-          (/**
-           * @return {!importer.PersistentImportHistory}
-           * @this {importer.PersistentImportHistory}
-           */
-          function() {
+          /**
+          * @return {!importer.PersistentImportHistory}
+          * @this {importer.PersistentImportHistory}
+          */
+          () => {
             return this;
-          }).bind(this))
+          })
       .catch(importer.getLogger().catcher('import-history-load'));
 };
 
@@ -275,15 +275,14 @@
   return this.whenReady_
       .then(this.createKey_.bind(this, entry))
       .then(
-          (/**
-           * @param {string} key
-           * @return {boolean}
-           * @this {importer.PersistentImportHistory}
-           */
-          function(key) {
+          /**
+          * @param {string} key
+          * @return {boolean}
+          */
+          key => {
             return key in this.copiedEntries_ &&
                 destination in this.copiedEntries_[key];
-          }).bind(this))
+          })
       .catch(importer.getLogger().catcher('import-history-was-imported'));
 };
 
@@ -293,14 +292,13 @@
   return this.whenReady_
       .then(this.createKey_.bind(this, entry))
       .then(
-          (/**
-           * @param {string} key
-           * @return {boolean}
-           * @this {importer.PersistentImportHistory}
-           */
-          function(key) {
+          /**
+          * @param {string} key
+          * @return {boolean}
+          */
+          key => {
             return this.getDestinations_(key).indexOf(destination) >= 0;
-          }).bind(this))
+          })
       .catch(importer.getLogger().catcher('import-history-was-imported'));
 };
 
@@ -309,19 +307,18 @@
     entry, destination, destinationUrl) {
   return this.whenReady_.then(this.createKey_.bind(this, entry))
       .then(
-          (/**
-           * @param {string} key
-           * @return {!Promise<?>}
-           * @this {importer.ImportHistory}
-           */
-          function(key) {
+          /**
+          * @param {string} key
+          * @return {!Promise<?>}
+          */
+          key => {
             return this.storeRecord_([
                 importer.RecordType_.COPY,
                 key,
                 destination,
                 importer.deflateAppUrl(entry.toURL()),
                 importer.deflateAppUrl(destinationUrl)]);
-          }).bind(this))
+          })
       .then(this.notifyObservers_.bind(
           this, importer.ImportHistoryState.COPIED, entry, destination,
           destinationUrl))
@@ -332,7 +329,7 @@
 importer.PersistentImportHistory.prototype.listUnimportedUrls =
     function(destination) {
   return this.whenReady_.then(
-      function() {
+      () => {
         // TODO(smckay): Merge copy and sync records for simpler
         // unimported file discovery.
         const unimported = [];
@@ -347,7 +344,7 @@
           }
         }
         return unimported;
-      }.bind(this))
+      })
       .catch(
           importer.getLogger().catcher('import-history-list-unimported-urls'));
 };
@@ -357,17 +354,16 @@
     entry, destination) {
   return this.whenReady_.then(this.createKey_.bind(this, entry))
       .then(
-          (/**
-           * @param {string} key
-           * @return {!Promise<?>}
-           * @this {importer.ImportHistory}
-           */
-          function(key) {
+          /**
+          * @param {string} key
+          * @return {!Promise<?>}
+          */
+          key => {
             return this.storeRecord_([
                 importer.RecordType_.IMPORT,
                 key,
                 destination]);
-          }).bind(this))
+          })
       .then(this.notifyObservers_.bind(
           this, importer.ImportHistoryState.IMPORTED, entry, destination))
       .catch(importer.getLogger().catcher('import-history-mark-imported'));
@@ -391,33 +387,31 @@
           key,
           destination])
             .then(
-                (/** @this {importer.PersistentImportHistory} */
-                function() {
+                () => {
                   const sourceUrl = importer.inflateAppUrl(
                       copyData[destination].sourceUrl);
                   // Here we try to create an Entry for the source URL.
                   // This will allow observers to update the UI if the
                   // source entry is in view.
                   util.urlToEntry(sourceUrl).then(
-                      (/**
-                       * @param {Entry} entry
-                       * @this {importer.PersistentImportHistory}
-                       */
-                      function(entry) {
+                      /**
+                      * @param {Entry} entry
+                      */
+                      entry => {
                         if (entry.isFile) {
                           this.notifyObservers_(
                               importer.ImportHistoryState.IMPORTED,
                               /** @type {!FileEntry} */ (entry), destination);
                         }
-                      }).bind(this),
-                      function() {
+                      },
+                      () => {
                         console.log(
                             'Unable to find original entry for: ' + sourceUrl);
                         return;
                       })
                       .catch(importer.getLogger().catcher(
                           'notify-listeners-on-import'));
-                }).bind(this))
+                })
             .catch(importer.getLogger().catcher('mark-imported-by-url'));
       }
     }
@@ -454,18 +448,18 @@
 importer.PersistentImportHistory.prototype.notifyObservers_ =
     function(state, entry, destination, opt_destinationUrl) {
   this.observers_.forEach(
-      (/**
-       * @param {!importer.ImportHistory.Observer} observer
-       * @this {importer.PersistentImportHistory}
-       */
-      function(observer) {
+      /**
+      * @param {!importer.ImportHistory.Observer} observer
+      * @this {importer.PersistentImportHistory}
+      */
+      observer => {
         observer({
           state: state,
           entry: entry,
           destination: destination,
           destinationUrl: opt_destinationUrl
         });
-      }).bind(this));
+      });
 };
 
 /**
@@ -522,21 +516,19 @@
   if (this.needsInitialization_) {
     this.needsInitialization_ = false;
     this.getHistoryFiles_()
-        .then((/**
-                * @param {!Array<!FileEntry>} fileEntries
-                * @this {importer.SynchronizedHistoryLoader}
-                */
-               function(fileEntries) {
-                 const storage = new importer.FileBasedRecordStorage(fileEntries);
-                 const history = new importer.PersistentImportHistory(
-                     importer.createMetadataHashcode, storage);
-                 new importer.DriveSyncWatcher(history);
-                 history.whenReady().then(
-                     (/** @this {importer.SynchronizedHistoryLoader} */
-                      function() {
-                        this.historyResolver_.resolve(history);
-                      }).bind(this));
-               }).bind(this))
+        .then(/**
+     * @param {!Array<!FileEntry>} fileEntries
+     */
+    fileEntries => {
+      const storage = new importer.FileBasedRecordStorage(fileEntries);
+      const history = new importer.PersistentImportHistory(
+          importer.createMetadataHashcode, storage);
+      new importer.DriveSyncWatcher(history);
+      history.whenReady().then(
+          () => {
+            this.historyResolver_.resolve(history);
+          });
+    })
         .catch(importer.getLogger().catcher('history-load-chain'));
   }
 
@@ -602,13 +594,12 @@
 importer.FileBasedRecordStorage.prototype.write = function(record) {
   return this.latestOperation_ = this.latestOperation_
       .then(
-          (/**
-           * @param {?} ignore
-           * @this {importer.FileBasedRecordStorage}
-           */
-          function(ignore) {
+          /**
+          * @param {?} ignore
+          */
+          ignore => {
             return this.outputFile_.createWriter();
-          }).bind(this))
+          })
       .then(this.writeRecord_.bind(this, record))
       .catch(importer.getLogger().catcher('file-record-store-write'));
 };
@@ -628,76 +619,72 @@
       {type: 'text/plain; charset=UTF-8'});
 
   return new Promise(
-      (/**
-       * @param {function()} resolve
-       * @param {function()} reject
-       * @this {importer.FileBasedRecordStorage}
-       */
-      function(resolve, reject) {
+      /**
+      * @param {function()} resolve
+      * @param {function()} reject
+      * @this {importer.FileBasedRecordStorage}
+      */
+      (resolve, reject) => {
         writer.onwriteend = resolve;
         writer.onerror = reject;
 
         writer.seek(writer.length);
         writer.write(blob);
-      }).bind(this));
+      });
 };
 
 /** @override */
 importer.FileBasedRecordStorage.prototype.readAll = function(recordCallback) {
   return this.latestOperation_ = this.latestOperation_
       .then(
-          (/**
-           * @param {?} ignored
-           * @this {importer.FileBasedRecordStorage}
-           */
-          function(ignored) {
+          /**
+          * @param {?} ignored
+          */
+          ignored => {
             const filePromises = this.inputFiles_.map(
                 /**
                  * @param {!importer.PromisingFileEntry} entry
                  * @this {importer.FileBasedRecordStorage}
                  */
-                function(entry) {
+                entry => {
                   return entry.file();
                 });
             return Promise.all(filePromises);
-          }).bind(this))
+          })
       .then(
-          (/**
-           * @return {!Promise<!Array<string>>}
-           * @this {importer.FileBasedRecordStorage}
-           */
-          function(files) {
+          /**
+          * @return {!Promise<!Array<string>>}
+          */
+          files => {
             const contentPromises = files.map(
                 this.readFileAsText_.bind(this));
             return Promise.all(contentPromises);
-          }).bind(this),
-          (/**
-           * @return {string}
-           * @this {importer.FileBasedRecordStorage}
-           */
-          function() {
+          },
+          /**
+          * @return {string}
+          */
+          () => {
             console.error('Unable to read from one of history files.');
             return '';
-          }).bind(this))
+          })
       .then(
-          (/**
-           * @param {!Array<string>} fileContents
-           * @this {importer.FileBasedRecordStorage}
-           */
-          function(fileContents) {
+          /**
+          * @param {!Array<string>} fileContents
+          */
+          fileContents => {
             const parsePromises = fileContents.map(
                 this.parse_.bind(this));
             return Promise.all(parsePromises);
-          }).bind(this))
+          })
       .then(
-          (/** @param {!Array<!Array<*>>} parsedContents */
-          function(parsedContents) {
+          /** @param {!Array<!Array<*>>} parsedContents */
+          parsedContents => {
             parsedContents.forEach(
                 /** @param {!Array<!Array<*>>} recordSet */
-                function(recordSet) {
+                recordSet => {
                   recordSet.forEach(recordCallback);
                 });
-          }).bind(this))
+          })
       .catch(importer.getLogger().catcher('file-record-store-read-all'));
 };
 
@@ -710,25 +697,25 @@
  */
 importer.FileBasedRecordStorage.prototype.readFileAsText_ = function(file) {
   return new Promise(
-      function(resolve, reject) {
+      (resolve, reject) => {
         const reader = new FileReader();
 
-        reader.onloadend = function() {
+        reader.onloadend = () => {
           if (reader.error) {
             console.error(reader.error);
             reject();
           } else {
             resolve(reader.result);
           }
-        }.bind(this);
+        };
 
-        reader.onerror = function(error) {
+        reader.onerror = error => {
           console.error(error);
           reject(error);
-        }.bind(this);
+        };
 
         reader.readAsText(file);
-      }.bind(this))
+      })
       .catch(importer.getLogger().catcher(
       'file-record-store-read-file-as-text'));
 };
@@ -740,7 +727,7 @@
  * @return {!Array<!Array<*>>}
  * @private
  */
-importer.FileBasedRecordStorage.prototype.parse_ = function(text) {
+importer.FileBasedRecordStorage.prototype.parse_ = text => {
   if (text.length === 0) {
     return [];
   } else {
@@ -774,12 +761,12 @@
 
   this.history_.whenReady()
       .then(
-          function() {
+          () => {
             this.history_.listUnimportedUrls(importer.Destination.GOOGLE_DRIVE)
                 .then(this.updateSyncStatus_.bind(
                     this,
                     importer.Destination.GOOGLE_DRIVE));
-          }.bind(this))
+          })
       .catch(importer.getLogger().catcher('drive-sync-watcher-constructor'));
 
   // Listener is only registered once the history object is initialized.
@@ -804,12 +791,12 @@
   // blocking interactive tasks. For now, we just defer the update
   // for a few seconds.
   setTimeout(
-      function() {
+      () => {
         unimportedUrls.forEach(
-            function(url) {
+            url => {
               this.checkSyncStatus_(destination, url);
-            }.bind(this));
-      }.bind(this),
+            });
+      },
       importer.DriveSyncWatcher.UPDATE_DELAY_MS);
 };
 
@@ -856,11 +843,10 @@
 
   this.getSyncStatus_(url)
       .then(
-          (/**
-           * @param {boolean} synced True if file is synced
-           * @this {importer.DriveSyncWatcher}
-           */
-          function(synced) {
+          /**
+          * @param {boolean} synced True if file is synced
+          */
+          synced => {
             if (synced) {
               if (opt_entry) {
                 this.history_.markImported(opt_entry, destination);
@@ -868,7 +854,7 @@
                 this.history_.markImportedByUrl(url);
               }
             }
-          }).bind(this))
+          })
       .catch(
           importer.getLogger().catcher(
               'drive-sync-watcher-check-sync-status'));
@@ -880,37 +866,37 @@
  *     file has been synced to the named destination.
  * @private
  */
-importer.DriveSyncWatcher.prototype.getSyncStatus_ = function(url) {
+importer.DriveSyncWatcher.prototype.getSyncStatus_ = url => {
   return util.URLsToEntries([url])
       .then(function(results) {
         if (results.entries.length !== 1) {
           return Promise.reject();
         }
         return new Promise(
-            (/** @this {importer.DriveSyncWatcher} */
-             function(resolve, reject) {
-               // TODO(smckay): User Metadata Cache...once it is available
-               // in the background.
-               chrome.fileManagerPrivate.getEntryProperties(
-                   [results.entries[0]], ['dirty'],
-                   (/**
-                     * @param
-                     * {!Array<!chrome.fileManagerPrivate.EntryProperties>|undefined}
-                     * propertiesList
-                     * @this {importer.DriveSyncWatcher}
-                     */
-                    function(propertiesList) {
-                      console.assert(
-                          propertiesList.length === 1,
-                          'Got an unexpected number of results.');
-                      if (chrome.runtime.lastError) {
-                        reject(chrome.runtime.lastError);
-                      } else {
-                        const data = propertiesList[0];
-                        resolve(!data['dirty']);
-                      }
-                    }).bind(this));
-             }).bind(this));
+            /** @this {importer.DriveSyncWatcher} */
+            (resolve, reject) => {
+              // TODO(smckay): User Metadata Cache...once it is available
+              // in the background.
+              chrome.fileManagerPrivate.getEntryProperties(
+                  [results.entries[0]], ['dirty'],
+                  /**
+                   * @param
+                   * {!Array<!chrome.fileManagerPrivate.EntryProperties>|undefined}
+                   * propertiesList
+                   * @this {importer.DriveSyncWatcher}
+                   */
+                  propertiesList => {
+                    console.assert(
+                        propertiesList.length === 1,
+                        'Got an unexpected number of results.');
+                    if (chrome.runtime.lastError) {
+                      reject(chrome.runtime.lastError);
+                    } else {
+                      const data = propertiesList[0];
+                      resolve(!data['dirty']);
+                    }
+                  });
+            });
       })
       .catch(
           importer.getLogger().catcher('drive-sync-watcher-get-sync-status'));
@@ -929,7 +915,7 @@
  */
 importer.RuntimeHistoryLoader = function() {
   /** @return {!importer.HistoryLoader} */
-  this.createRealHistoryLoader_ = function() {
+  this.createRealHistoryLoader_ = () => {
     return new importer.SynchronizedHistoryLoader(importer.getHistoryFiles);
   };
 
@@ -946,20 +932,21 @@
     this.needsInitialization_ = false;
     importer.importEnabled()
         .then(
-            (/**
-             * @param {boolean} enabled
-             * @return {!importer.HistoryLoader}
-             * @this {importer.RuntimeHistoryLoader}
-             */
-            function(enabled) {
+            /**
+            * @param {boolean} enabled
+            * @return {!importer.HistoryLoader}
+            */
+            enabled => {
               return enabled ?
                   this.createRealHistoryLoader_() :
                   new importer.DummyImportHistory(false);
-            }).bind(this))
+            })
         .then(
-            function(loader) {
-              return this.historyResolver_.resolve(loader.getHistory());
-            }.bind(this))
+            loader => {
+              return this.historyResolver_.resolve(
+                  /** @type {!importer.ImportHistory} */
+                      (loader.getHistory()));
+            })
         .catch(
             importer.getLogger().catcher(
                 'runtime-history-loader-get-history'));
@@ -981,13 +968,12 @@
  */
 importer.createMetadataHashcode = function(fileEntry) {
   return new Promise(
-             function(resolve, reject) {
+             (resolve, reject) => {
                metadataProxy.getEntryMetadata(fileEntry).then(
-                   (/**
-                    * @param {!Object} metadata
-                    * @this {importer.PersistentImportHistory}
-                    */
-                   function(metadata) {
+                   /**
+                   * @param {!Object} metadata
+                   */
+                   metadata => {
                      if (!('modificationTime' in metadata)) {
                        reject('File entry missing "modificationTime" field.');
                      } else if (!('size' in metadata)) {
@@ -997,7 +983,7 @@
                            metadata.modificationTime);
                        resolve(secondsSinceEpoch + '_' + metadata.size);
                      }
-                   }).bind(this));
-             }.bind(this))
+                   });
+             })
       .catch(importer.getLogger().catcher('importer-common-create-hashcode'));
 };
diff --git a/ui/file_manager/file_manager/background/js/import_history_unittest.js b/ui/file_manager/file_manager/background/js/import_history_unittest.js
index b9bc68fe..af8de79 100644
--- a/ui/file_manager/file_manager/background/js/import_history_unittest.js
+++ b/ui/file_manager/file_manager/background/js/import_history_unittest.js
@@ -75,7 +75,7 @@
   // TestRecordWriter is pre-configured with a Space Cloud entry
   // but not for this file.
   testPromise = historyProvider.then(
-      function(history) {
+      history => {
         return history.wasCopied(testFileEntry, SPACE_CAMP).then(assertFalse);
       });
 
@@ -85,7 +85,7 @@
 function testWasCopied_TrueForKnownEntryLoadedFromStorage(callback) {
   // TestRecordWriter is pre-configured with this entry.
   testPromise = historyProvider.then(
-      function(history) {
+      history => {
         return history.wasCopied(testFileEntry, GOOGLE_DRIVE).then(assertTrue);
       });
 
@@ -95,14 +95,14 @@
 
 function testMarkCopied_FiresChangedEvent(callback) {
   testPromise = historyProvider.then(
-      function(history) {
+      history => {
         const recorder = new TestCallRecorder();
         history.addObserver(recorder.callback);
         return history.markCopied(testFileEntry, SPACE_CAMP, 'url1').then(
-            function() {
+            () => {
               return Promise.resolve()
                   .then(
-                      function() {
+                      () => {
                         recorder.assertCallCount(1);
                         assertEquals(
                             importer.ImportHistoryState.COPIED,
@@ -117,13 +117,13 @@
 function testMarkImported_ByUrl(callback) {
   const destinationUrl = 'filesystem:chrome-extension://abc/photos/splosion.jpg';
   testPromise = historyProvider.then(
-      function(history) {
+      history => {
         return history.markCopied(testFileEntry, SPACE_CAMP, destinationUrl)
             .then(
-                function() {
+                () => {
                   return history.markImportedByUrl(destinationUrl)
                       .then(
-                          function() {
+                          () => {
                             return history.wasImported(
                                 testFileEntry,
                                 SPACE_CAMP)
@@ -139,7 +139,7 @@
   // TestRecordWriter is pre-configured with a Space Cloud entry
   // but not for this file.
   testPromise = historyProvider.then(
-      function(history) {
+      history => {
         return history.wasImported(testFileEntry, SPACE_CAMP).then(assertFalse);
       });
 
@@ -149,7 +149,7 @@
 function testWasImported_TrueForKnownEntryLoadedFromStorage(callback) {
   // TestRecordWriter is pre-configured with this entry.
   testPromise = historyProvider.then(
-      function(history) {
+      history => {
         return history.wasImported(testFileEntry, GOOGLE_DRIVE)
             .then(assertTrue);
       });
@@ -159,9 +159,9 @@
 
 function testWasImported_TrueForKnownEntrySetAtRuntime(callback) {
   testPromise = historyProvider.then(
-      function(history) {
+      history => {
         return history.markImported(testFileEntry, SPACE_CAMP).then(
-            function() {
+            () => {
               return history.wasImported(testFileEntry, SPACE_CAMP)
                   .then(assertTrue);
             });
@@ -172,14 +172,14 @@
 
 function testMarkImport_FiresChangedEvent(callback) {
   testPromise = historyProvider.then(
-      function(history) {
+      history => {
         const recorder = new TestCallRecorder();
         history.addObserver(recorder.callback);
         return history.markImported(testFileEntry, SPACE_CAMP).then(
-            function() {
+            () => {
               return Promise.resolve()
                   .then(
-                      function() {
+                      () => {
                         recorder.assertCallCount(1);
                         assertEquals(
                             importer.ImportHistoryState.IMPORTED,
@@ -193,7 +193,7 @@
 
 function testHistoryObserver_Unsubscribe(callback) {
   testPromise = historyProvider.then(
-      function(history) {
+      history => {
         const recorder = new TestCallRecorder();
         history.addObserver(recorder.callback);
         history.removeObserver(recorder.callback);
@@ -202,10 +202,10 @@
         promises.push(history.markCopied(testFileEntry, SPACE_CAMP, 'url2'));
         promises.push(history.markImported(testFileEntry, SPACE_CAMP));
         return Promise.all(promises).then(
-            function() {
+            () => {
               return Promise.resolve()
                   .then(
-                      function() {
+                      () => {
                         recorder.assertCallCount(0);
                       });
             });
@@ -218,11 +218,11 @@
   const recorder = new TestCallRecorder();
   testPromise = createRealStorage(['recordStorageTest.data'])
       .then(
-          function(storage) {
+          storage => {
             return storage.write(['abc', '123']).then(
-                function() {
+                () => {
                   return storage.readAll(recorder.callback).then(
-                      function() {
+                      () => {
                         recorder.assertCallCount(1);
                       });
                 });
@@ -236,31 +236,31 @@
 
   const remoteData = createRealStorage(['multiStorage-1.data'])
       .then(
-          function(storage) {
+          storage => {
             return storage.write(['remote-data', '98765432']);
           });
   const moreRemoteData = createRealStorage(['multiStorage-2.data'])
       .then(
-          function(storage) {
+          storage => {
             return storage.write(['antarctica-data', '777777777777']);
           });
 
   testPromise = Promise.all([remoteData, moreRemoteData]).then(
-    function() {
+    () => {
       return createRealStorage([
         'multiStorage-0.data',
         'multiStorage-1.data',
         'multiStorage-2.data'])
         .then(
-            function(storage) {
+            storage => {
               const writePromises = [
                 storage.write(['local-data', '111'])
               ];
               return Promise.all(writePromises)
                   .then(
-                      function() {
+                      () => {
                         return storage.readAll(recorder.callback).then(
-                            function() {
+                            () => {
                               recorder.assertCallCount(3);
                               assertEquals(
                                   'local-data',
@@ -283,14 +283,14 @@
   const recorder = new TestCallRecorder();
   testPromise = createRealStorage(['recordStorageTestForSerializing.data'])
       .then(
-          function(storage) {
+          storage => {
             const writePromises = [];
             const WRITES_COUNT = 20;
             for (let i = 0; i < WRITES_COUNT; i++) {
               writePromises.push(storage.write(['abc', '123']));
             }
             const readAllPromise = storage.readAll(recorder.callback).then(
-              function() {
+              () => {
                 recorder.assertCallCount(WRITES_COUNT);
               });
             // Write an extra record, which must be executed afte reading is
@@ -304,7 +304,7 @@
 
 function testCreateMetadataHashcode(callback) {
   const promise =
-      importer.createMetadataHashcode(testFileEntry).then(function(hashcode) {
+      importer.createMetadataHashcode(testFileEntry).then(hashcode => {
         // Note that the expression matches at least 4 numbers
         // in the last segment, since we hard code the byte
         // size in our test file to a four digit size.
@@ -329,7 +329,7 @@
  */
 function installTestLogger() {
   testLogger = new importer.TestLogger();
-  importer.getLogger = function() {
+  importer.getLogger = () => {
     return testLogger;
   };
 }
@@ -340,7 +340,7 @@
  */
 function createRealStorage(fileNames) {
   const filePromises = fileNames.map(createFileEntry);
-  return Promise.all(filePromises).then(function(fileEntries) {
+  return Promise.all(filePromises).then(fileEntries => {
     return new importer.FileBasedRecordStorage(fileEntries);
   });
 }
@@ -353,8 +353,8 @@
  */
 function createFileEntry(fileName) {
   return new Promise(
-      function(resolve, reject) {
-        const onFileSystemReady = function(fileSystem) {
+      (resolve, reject) => {
+        const onFileSystemReady = fileSystem => {
           fileSystem.root.getFile(
               fileName,
               {
diff --git a/ui/file_manager/file_manager/background/js/launcher.js b/ui/file_manager/file_manager/background/js/launcher.js
index ed90d714..a360b33 100644
--- a/ui/file_manager/file_manager/background/js/launcher.js
+++ b/ui/file_manager/file_manager/background/js/launcher.js
@@ -67,8 +67,7 @@
  * @param {LaunchType=} opt_type Launch type. Default: ALWAYS_CREATE.
  * @param {function(string)=} opt_callback Completion callback with the App ID.
  */
-launcher.launchFileManager = function(
-    opt_appState, opt_id, opt_type, opt_callback) {
+launcher.launchFileManager = (opt_appState, opt_id, opt_type, opt_callback) => {
   const type = opt_type || LaunchType.ALWAYS_CREATE;
   opt_appState =
       /**
@@ -79,7 +78,7 @@
       (opt_appState);
 
   // Wait until all windows are created.
-  launcher.queue.run(function(onTaskCompleted) {
+  launcher.queue.run(onTaskCompleted => {
     // Check if there is already a window with the same URL. If so, then
     // reuse it instead of opening a new one.
     if (type == LaunchType.FOCUS_SAME_OR_CREATE ||
@@ -177,7 +176,7 @@
         'main.html',
         appId,
         FILE_MANAGER_WINDOW_CREATE_OPTIONS);
-    appWindow.launch(opt_appState || {}, false, function() {
+    appWindow.launch(opt_appState || {}, false, () => {
       appWindow.rawAppWindow.focus();
       if (opt_callback) {
         opt_callback(appId);
diff --git a/ui/file_manager/file_manager/background/js/launcher_search.js b/ui/file_manager/file_manager/background/js/launcher_search.js
index 6eebcf8..702af4ad 100644
--- a/ui/file_manager/file_manager/background/js/launcher_search.js
+++ b/ui/file_manager/file_manager/background/js/launcher_search.js
@@ -50,10 +50,10 @@
  * Handles onPreferencesChanged event.
  */
 LauncherSearch.prototype.onPreferencesChanged_ = function() {
-  chrome.fileManagerPrivate.getPreferences(function(preferences) {
+  chrome.fileManagerPrivate.getPreferences(preferences => {
     this.initializeEventListeners_(
         preferences.driveEnabled, preferences.searchSuggestEnabled);
-  }.bind(this));
+  });
 };
 
 /**
@@ -154,8 +154,8 @@
   // Request an instance of volume manager to ensure that all volumes are
   // initialized. webkitResolveLocalFileSystemURL in util.urlToEntry fails if
   // filesystem of the url is not initialized.
-  volumeManagerFactory.getInstance().then(function() {
-    util.urlToEntry(itemId).then(function(entry) {
+  volumeManagerFactory.getInstance().then(() => {
+    util.urlToEntry(itemId).then(entry => {
       if (entry.isDirectory) {
         // If it's directory, open the directory with file manager.
         launcher.launchFileManager(
@@ -168,7 +168,7 @@
           return;
         }
         // If the file is not directory, try to execute default task.
-        chrome.fileManagerPrivate.getFileTasks([entry], function(tasks) {
+        chrome.fileManagerPrivate.getFileTasks([entry], tasks => {
           // Select default task.
           let defaultTask = null;
           for (var i = 0; i < tasks.length; i++) {
@@ -195,22 +195,22 @@
           if (defaultTask) {
             // Execute default task.
             chrome.fileManagerPrivate.executeTask(
-                defaultTask.taskId, [entry], function(result) {
+                defaultTask.taskId, [entry], result => {
                   if (result === 'opened' || result === 'message_sent') {
                     return;
                   }
                   this.openFileManagerWithSelectionURL_(entry.toURL());
-                }.bind(this));
+                });
           } else {
             // If there is no default task for the url, open a file manager with
             // selecting it.
             // TODO(yawano): Add fallback to view-in-browser as file_tasks.js do
             this.openFileManagerWithSelectionURL_(entry.toURL());
           }
-        }.bind(this));
+        });
       }
-    }.bind(this));
-  }.bind(this));
+    });
+  });
 };
 
 /**
@@ -218,8 +218,7 @@
  * @param {string} selectionURL A url to be selected.
  * @private
  */
-LauncherSearch.prototype.openFileManagerWithSelectionURL_ = function(
-    selectionURL) {
+LauncherSearch.prototype.openFileManagerWithSelectionURL_ = selectionURL => {
   launcher.launchFileManager(
       {selectionURL: selectionURL},
       undefined, /* App ID */
@@ -234,10 +233,10 @@
  * @return {!Promise<!Array<!Entry>>}
  * @private
  */
-LauncherSearch.prototype.queryDriveEntries_ = function(queryId, query, limit) {
+LauncherSearch.prototype.queryDriveEntries_ = (queryId, query, limit) => {
   const param = {query: query, types: 'ALL', maxResults: limit};
   return new Promise((resolve, reject) => {
-    chrome.fileManagerPrivate.searchDriveMetadata(param, function(results) {
+    chrome.fileManagerPrivate.searchDriveMetadata(param, results => {
       resolve(results.map(result => result.entry));
     });
   });
@@ -273,7 +272,7 @@
  * @return {!Promise<!DirectoryEntry>}
  * @private
  */
-LauncherSearch.prototype.getDownloadsEntry_ = function() {
+LauncherSearch.prototype.getDownloadsEntry_ = () => {
   return volumeManagerFactory.getInstance().then((volumeManager) => {
     const downloadsVolumeInfo = volumeManager.getCurrentProfileVolumeInfo(
         VolumeManagerCommon.VolumeType.DOWNLOADS);
@@ -320,7 +319,7 @@
  * @return {!Array<!Entry>}
  * @private
  */
-LauncherSearch.prototype.chooseEntries_ = function(entries, query, limit) {
+LauncherSearch.prototype.chooseEntries_ = (entries, query, limit) => {
   query = query.toLowerCase();
   const scoreEntry = (entry) => {
     // Prefer entry which has the query string as a prefix.
@@ -341,7 +340,7 @@
  * @return {!Object}
  * @private
  */
-LauncherSearch.prototype.createSearchResult_ = function(entry) {
+LauncherSearch.prototype.createSearchResult_ = entry => {
   // TODO(yawano): Use filetype_folder_shared.png for a shared
   //     folder.
   // TODO(yawano): Add archive launcher filetype icon.
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler.js b/ui/file_manager/file_manager/background/js/media_import_handler.js
index c4352f95..3402d07 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler.js
+++ b/ui/file_manager/file_manager/background/js/media_import_handler.js
@@ -29,10 +29,10 @@
   this.queue_ = new importer.TaskQueue();
 
   // Prevent the system from sleeping while imports are active.
-  this.queue_.setActiveCallback(function() {
+  this.queue_.setActiveCallback(() => {
     chrome.power.requestKeepAwake('system');
   });
-  this.queue_.setIdleCallback(function() {
+  this.queue_.setIdleCallback(() => {
     chrome.power.releaseKeepAwake();
   });
 
@@ -107,7 +107,7 @@
     // TODO(kenobi): Might need a different progress item type here.
     item.type = ProgressItemType.COPY;
     item.progressMax = task.totalBytes;
-    item.cancelCallback = function() {
+    item.cancelCallback = () => {
       task.requestCancel();
     };
   }
@@ -134,7 +134,7 @@
         // Otherwise, finish progress bar.
         // Display all errors.
         let errorIdCounter = 0;
-        task.failedEntries.forEach(function(entry) {
+        task.failedEntries.forEach(entry => {
           const errorItem = new ProgressCenterItem();
           errorItem.id = task.taskId_ + '-' + (errorIdCounter++);
           errorItem.type = ProgressItemType.COPY;
@@ -142,7 +142,7 @@
           errorItem.state = ProgressItemState.ERROR;
           errorItem.message = strf('CLOUD_IMPORT_ERROR_ITEM', entry.name);
           this.progressCenter_.updateItem(item);
-        }.bind(this));
+        });
 
         // Complete progress bar.
         item.message = '';
@@ -189,7 +189,7 @@
  * @param {Object=} updateInfo
  */
 importer.MediaImportHandler.prototype.onFileImported_ =
-    function(task, updateType, updateInfo) {
+    (task, updateType, updateInfo) => {
   if (updateType !==
       importer.MediaImportHandler.ImportTask.UpdateType.ENTRY_CHANGED) {
     return;
@@ -206,7 +206,7 @@
       'private',  // Scoped to just this app.
       importer.MediaImportHandler.IMPORTS_TAG_KEY,
       importer.MediaImportHandler.IMPORTS_TAG_VALUE,
-      function() {
+      () => {
         if (chrome.runtime.lastError) {
           console.error('Unable to tag imported media: ' +
               chrome.runtime.lastError.message);
@@ -376,10 +376,10 @@
  */
 importer.MediaImportHandler.ImportTask.prototype.requestCancel = function() {
   this.canceled_ = true;
-  setTimeout(function() {
+  setTimeout(() => {
     this.notify(importer.TaskQueue.UpdateType.CANCELED);
     this.sendImportStats_();
-  }.bind(this));
+  });
   if (this.cancelCallback_) {
     // Reset the callback before calling it, as the callback might do anything
     // (including calling #requestCancel again).
@@ -407,11 +407,11 @@
 importer.MediaImportHandler.ImportTask.prototype.importScanEntries_ =
     function() {
   const resolver = new importer.Resolver();
-  this.directoryPromise_.then(function(destinationDirectory) {
+  this.directoryPromise_.then(destinationDirectory => {
     AsyncUtil.forEach(
         this.importEntries_, this.importOne_.bind(this, destinationDirectory),
-        resolver.resolve, resolver);
-  }.bind(this));
+        resolver.resolve);
+  });
   return resolver.promise;
 };
 
@@ -423,20 +423,19 @@
 importer.MediaImportHandler.ImportTask.prototype.markDuplicatesImported_ =
     function() {
   this.historyLoader_.getHistory().then(
-      (/**
-       * @param {!importer.ImportHistory} history
-       * @this {importer.MediaImportHandler.ImportTask}
-       */
-      function(history) {
+      /**
+      * @param {!importer.ImportHistory} history
+      */
+      history => {
         this.scanResult_.getDuplicateFileEntries().forEach(
-            (/**
-             * @param {!FileEntry} entry
-             * @this {importer.MediaImportHandler.ImportTask}
-             */
-            function(entry) {
+            /**
+            * @param {!FileEntry} entry
+            * @this {importer.MediaImportHandler.ImportTask}
+            */
+            entry => {
               history.markImported(entry, this.destination_);
-            }).bind(this));
-      }).bind(this))
+            });
+      })
       .catch(importer.getLogger().catcher('import-task-mark-dupes-imported'));
 };
 
@@ -460,31 +459,28 @@
 
   this.getDisposition_(
           entry, importer.Destination.GOOGLE_DRIVE, importer.ScanMode.CONTENT)
-      .then((/**
-              * @param {!importer.Disposition} disposition The disposition
-              *     of the entry. Either some sort of dupe, or an original.
-              * @this {importer.MediaImportHandler.ImportTask}
-              */
-             function(disposition) {
-               if (disposition === importer.Disposition.ORIGINAL) {
-                 return this.copy_(entry, destinationDirectory);
-               }
-               this.duplicateFilesCount_++;
-               this.markAsImported_(entry);
-             }).bind(this))
+      .then(/**
+   * @param {!importer.Disposition} disposition The disposition
+   *     of the entry. Either some sort of dupe, or an original.
+   */
+  disposition => {
+    if (disposition === importer.Disposition.ORIGINAL) {
+      return this.copy_(entry, destinationDirectory);
+    }
+    this.duplicateFilesCount_++;
+    this.markAsImported_(entry);
+  })
       // Regardless of the result of this copy, push on to the next file.
       .then(completionCallback)
-      .catch((
-                 /** @param {*} error */
-                 function(error) {
-                   importer.getLogger().catcher('import-task-import-one')(
-                       error);
-                   // TODO(oka): Retry copies only when failed due to
-                   // insufficient disk space. crbug.com/788692.
-                   this.failedEntries_.push(entry);
-                   completionCallback();
-                 })
-                 .bind(this));
+      .catch(/** @param {*} error */
+  error => {
+    importer.getLogger().catcher('import-task-import-one')(
+        error);
+    // TODO(oka): Retry copies only when failed due to
+    // insufficient disk space. crbug.com/788692.
+    this.failedEntries_.push(entry);
+    completionCallback();
+  });
 };
 
 /**
@@ -508,7 +504,7 @@
    * @param {number} processedBytes
    * @this {importer.MediaImportHandler.ImportTask}
    */
-  const onProgress = function(sourceUrl, processedBytes) {
+  const onProgress = (sourceUrl, processedBytes) => {
     // Update the running total, then send a progress update.
     this.processedBytes_ -= currentBytes;
     this.processedBytes_ += processedBytes;
@@ -522,12 +518,13 @@
    * @param {Entry} destinationEntry
    * @this {importer.MediaImportHandler.ImportTask}
    */
-  const onEntryChanged = function(sourceUrl, destinationEntry) {
+  const onEntryChanged = (sourceUrl, destinationEntry) => {
     this.processedBytes_ -= currentBytes;
     this.processedBytes_ += entry.size;
     destinationEntry.size = entry.size;
     this.notify(
-        importer.MediaImportHandler.ImportTask.UpdateType.ENTRY_CHANGED,
+        /** @type {importer.TaskQueue.UpdateType} */
+            (importer.MediaImportHandler.ImportTask.UpdateType.ENTRY_CHANGED),
         {
           sourceUrl: sourceUrl,
           destination: destinationEntry
@@ -539,7 +536,7 @@
    * @param {Entry} destinationEntry The new destination entry.
    * @this {importer.MediaImportHandler.ImportTask}
    */
-  const onComplete = function(destinationEntry) {
+  const onComplete = destinationEntry => {
     this.cancelCallback_ = null;
     this.markAsCopied_(entry, /** @type {!FileEntry} */ (destinationEntry));
     this.notify(importer.TaskQueue.UpdateType.PROGRESS);
@@ -547,7 +544,7 @@
   };
 
   /** @this {importer.MediaImportHandler.ImportTask} */
-  const onError = function(error) {
+  const onError = error => {
     this.cancelCallback_ = null;
     if (error.name === util.FileError.ABORT_ERR) {
       // Task cancellations result in the error callback being triggered with an
@@ -563,21 +560,20 @@
 
   fileOperationUtil.deduplicatePath(destinationDirectory, entry.name)
       .then(
-          (/**
-           * Performs the copy using the given deduped filename.
-           * @param {string} destinationFilename
-           * @this {importer.MediaImportHandler.ImportTask}
-           */
-          function(destinationFilename) {
+          /**
+          * Performs the copy using the given deduped filename.
+          * @param {string} destinationFilename
+          */
+          destinationFilename => {
             this.cancelCallback_ = fileOperationUtil.copyTo(
                 entry,
                 destinationDirectory,
                 destinationFilename,
-                onEntryChanged.bind(this),
-                onProgress.bind(this),
-                onComplete.bind(this),
-                onError.bind(this));
-          }).bind(this),
+                onEntryChanged,
+                onProgress,
+                onComplete,
+                onError);
+          },
           resolver.reject)
       .catch(importer.getLogger().catcher('import-task-copy'));
 
@@ -592,16 +588,12 @@
     function(entry, destinationEntry) {
   this.remainingFilesCount_--;
   this.historyLoader_.getHistory().then(
-      (/**
-       * @param {!importer.ImportHistory} history
-       * @this {importer.MediaImportHandler.ImportTask}
-       */
-      function(history) {
+      history => {
         history.markCopied(
             entry,
             this.destination_,
             destinationEntry.toURL());
-      }).bind(this))
+      })
       .catch(importer.getLogger().catcher('import-task-mark-as-copied'));
 };
 
@@ -613,10 +605,10 @@
     function(entry) {
   this.remainingFilesCount_--;
   this.historyLoader_.getHistory().then(
-      (/** @param {!importer.ImportHistory} history */
-      function(history) {
+      /** @param {!importer.ImportHistory} history */
+      history => {
         history.markImported(entry, this.destination_);
-      }).bind(this))
+      })
       .catch(importer.getLogger().catcher('import-task-mark-as-imported'));
 };
 
@@ -655,7 +647,7 @@
       this.duplicateFilesCount_;
 
   Object.keys(scanStats.duplicates).forEach(
-      function(disposition) {
+      disposition => {
         const count = scanStats.duplicates[
             /** @type {!importer.Disposition} */ (disposition)];
         totalDeduped += count;
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
index 3f692a23..52df8ca 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
@@ -88,7 +88,7 @@
 
   // Setup a default disposition checker. Tests can replace it at runtime
   // if they need specialized disposition check behavior.
-  dispositionChecker = function() {
+  dispositionChecker = () => {
     return Promise.resolve(importer.Disposition.ORIGINAL);
   };
 
@@ -124,13 +124,13 @@
       importer.Destination.GOOGLE_DRIVE,
       destinationFactory);
 
-  const whenImportDone = new Promise(function(resolve, reject) {
+  const whenImportDone = new Promise((resolve, reject) => {
     importTask.addObserver(
         /**
          * @param {!importer.TaskQueue.UpdateType} updateType
          * @param {Object=} opt_task
          */
-        function(updateType, opt_task) {
+        (updateType, opt_task) => {
           switch (updateType) {
             case importer.TaskQueue.UpdateType.COMPLETE:
               resolve();
@@ -143,7 +143,7 @@
   });
 
   reportPromise(
-      whenImportDone.then(function() {
+      whenImportDone.then(() => {
         const mockDirectoryEntry =
             /** @type {!MockDirectoryEntry} */ (destinationFileSystem.root);
         const copiedEntries = mockDirectoryEntry.getAllChildren();
@@ -169,7 +169,7 @@
     DUPLICATED_FILE_PATH_2,
   ]);
 
-  dispositionChecker = function(entry, destination) {
+  dispositionChecker = (entry, destination) => {
     if (entry.fullPath == DUPLICATED_FILE_PATH_1) {
       return Promise.resolve(importer.Disposition.HISTORY_DUPLICATE);
     }
@@ -186,13 +186,13 @@
       importer.Destination.GOOGLE_DRIVE,
       destinationFactory);
 
-  const whenImportDone = new Promise(function(resolve, reject) {
+  const whenImportDone = new Promise((resolve, reject) => {
     importTask.addObserver(
         /**
          * @param {!importer.TaskQueue.UpdateType} updateType
          * @param {Object=} opt_task
          */
-        function(updateType, opt_task) {
+        (updateType, opt_task) => {
           switch (updateType) {
             case importer.TaskQueue.UpdateType.COMPLETE:
               resolve();
@@ -206,7 +206,7 @@
 
   reportPromise(
       whenImportDone.then(
-          function() {
+          () => {
             // Only the new file should be copied.
             const mockDirectoryEntry =
                 /** @type {!MockDirectoryEntry} */ (destinationFileSystem.root);
@@ -217,7 +217,7 @@
             importHistory.assertCopied(
                 mockFileEntry, importer.Destination.GOOGLE_DRIVE);
             // The 2 duplicated files should be marked as imported.
-            [media[0], media[2]].forEach(function(entry) {
+            [media[0], media[2]].forEach(entry => {
               entry = /** @type {!MockFileEntry} */ (entry);
               importHistory.assertImported(
                   entry, importer.Destination.GOOGLE_DRIVE);
@@ -242,13 +242,13 @@
       destinationFactory);
 
   const promise =
-      new Promise(function(resolve, reject) {
+      new Promise((resolve, reject) => {
         importTask.addObserver(
             /**
              * @param {!importer.TaskQueue.UpdateType} updateType
              * @param {Object=} opt_task
              */
-            function(updateType, opt_task) {
+            (updateType, opt_task) => {
               switch (updateType) {
                 case importer.TaskQueue.UpdateType.COMPLETE:
                   resolve(/** @type {!MockDirectoryEntry} */
@@ -259,7 +259,7 @@
                   break;
               }
             });
-      }).then(function(copiedEntries) {
+      }).then(copiedEntries => {
         const expected = 'Mom%20and%20Dad.jpg';
         const url = copiedEntries[0].toURL();
         assertTrue(url.length > expected.length);
@@ -292,13 +292,13 @@
       importer.Destination.GOOGLE_DRIVE,
       destinationFactory);
 
-  const whenImportDone = new Promise(function(resolve, reject) {
+  const whenImportDone = new Promise((resolve, reject) => {
     importTask.addObserver(
         /**
          * @param {!importer.TaskQueue.UpdateType} updateType
          * @param {Object=} opt_task
          */
-        function(updateType, opt_task) {
+        (updateType, opt_task) => {
           switch (updateType) {
             case importer.TaskQueue.UpdateType.COMPLETE:
               resolve();
@@ -312,7 +312,7 @@
 
   // Verify that we end up with 6, and not 3, destination entries.
   reportPromise(
-      whenImportDone.then(function() {
+      whenImportDone.then(() => {
         const mockDirectoryEntry =
             /** @type {!MockDirectoryEntry} */ (destinationFileSystem.root);
         const copiedEntries = mockDirectoryEntry.getAllChildren();
@@ -342,13 +342,13 @@
       importer.Destination.GOOGLE_DRIVE,
       destinationFactory);
 
-  const whenImportDone = new Promise(function(resolve, reject) {
+  const whenImportDone = new Promise((resolve, reject) => {
     importTask.addObserver(
         /**
          * @param {!importer.TaskQueue.UpdateType} updateType
          * @param {Object=} opt_task
          */
-        function(updateType, opt_task) {
+        (updateType, opt_task) => {
           // Assert that keepAwake is set while the task is active.
           assertTrue(mockChrome.power.requestKeepAwakeStatus);
           switch (updateType) {
@@ -363,7 +363,7 @@
   });
 
   reportPromise(
-      whenImportDone.then(function() {
+      whenImportDone.then(() => {
         assertTrue(mockChrome.power.requestKeepAwakeWasCalled);
         assertFalse(mockChrome.power.requestKeepAwakeStatus);
         const mockDirectoryEntry =
@@ -397,13 +397,13 @@
       importer.Destination.GOOGLE_DRIVE,
       destinationFactory);
 
-  const whenImportDone = new Promise(function(resolve, reject) {
+  const whenImportDone = new Promise((resolve, reject) => {
     importTask.addObserver(
         /**
          * @param {!importer.TaskQueue.UpdateType} updateType
          * @param {Object=} opt_task
          */
-        function(updateType, opt_task) {
+        (updateType, opt_task) => {
           switch (updateType) {
             case importer.TaskQueue.UpdateType.COMPLETE:
               resolve();
@@ -415,15 +415,15 @@
         });
   });
 
-  const promise = whenImportDone.then(function() {
+  const promise = whenImportDone.then(() => {
     mockCopier.copiedFiles.forEach(
         /** @param {!MockCopyTo.CopyInfo} copy */
-        function(copy) {
+        copy => {
           const mockFileEntry = /** @type {!MockFileEntry} */ (copy.source);
           importHistory.assertCopied(
               mockFileEntry, importer.Destination.GOOGLE_DRIVE);
         });
-    dupeFiles.forEach(function(entry) {
+    dupeFiles.forEach(entry => {
       const mockFileEntry = /** @type {!MockFileEntry} */ (entry);
       importHistory.assertImported(
           mockFileEntry, importer.Destination.GOOGLE_DRIVE);
@@ -449,13 +449,13 @@
       importer.Destination.GOOGLE_DRIVE,
       destinationFactory);
 
-  const whenImportDone = new Promise(function(resolve, reject) {
+  const whenImportDone = new Promise((resolve, reject) => {
     importTask.addObserver(
         /**
          * @param {!importer.TaskQueue.UpdateType} updateType
          * @param {Object=} opt_task
          */
-        function(updateType, opt_task) {
+        (updateType, opt_task) => {
           switch (updateType) {
             case importer.TaskQueue.UpdateType.COMPLETE:
               resolve();
@@ -469,13 +469,13 @@
 
   const taggedEntries = [];
   // Replace chrome.fileManagerPrivate.setEntryTag with a listener.
-  mockChrome.fileManagerPrivate.setEntryTag = function(entry) {
+  mockChrome.fileManagerPrivate.setEntryTag = entry => {
     taggedEntries.push(entry);
   };
 
   reportPromise(
       whenImportDone.then(
-          function() {
+          () => {
             assertEquals(entries.length, taggedEntries.length);
           }),
       callback);
@@ -505,13 +505,13 @@
       importer.Destination.GOOGLE_DRIVE,
       destinationFactory);
 
-  const whenImportCancelled = new Promise(function(resolve, reject) {
+  const whenImportCancelled = new Promise((resolve, reject) => {
     importTask.addObserver(
         /**
          * @param {!importer.TaskQueue.UpdateType} updateType
          * @param {Object=} opt_task
          */
-        function(updateType, opt_task) {
+        (updateType, opt_task) => {
           if (updateType === importer.TaskQueue.UpdateType.CANCELED) {
             resolve();
           }
@@ -520,7 +520,7 @@
 
   // Simulate cancellation after the expected number of copies is done.
   let copyCount = 0;
-  importTask.addObserver(function(updateType) {
+  importTask.addObserver(updateType => {
     if (updateType ===
         importer.MediaImportHandler.ImportTask.UpdateType.ENTRY_CHANGED) {
       copyCount++;
@@ -531,7 +531,7 @@
   });
 
   reportPromise(
-      whenImportCancelled.then(function() {
+      whenImportCancelled.then(() => {
         const mockDirectoryEntry =
             /** @type {!MockDirectoryEntry} */ (destinationFileSystem.root);
         const copiedEntries = mockDirectoryEntry.getAllChildren();
@@ -568,13 +568,13 @@
       importer.Destination.GOOGLE_DRIVE,
       destinationFactory);
 
-  const whenImportDone = new Promise(function(resolve, reject) {
+  const whenImportDone = new Promise((resolve, reject) => {
     importTask.addObserver(
         /**
          * @param {!importer.TaskQueue.UpdateType} updateType
          * @param {Object=} opt_task
          */
-        function(updateType, opt_task) {
+        (updateType, opt_task) => {
           if (updateType === importer.TaskQueue.UpdateType.COMPLETE) {
             resolve();
           }
@@ -583,7 +583,7 @@
 
   // Simulate an error after 3 imports.
   let copyCount = 0;
-  importTask.addObserver(function(updateType) {
+  importTask.addObserver(updateType => {
     if (updateType ===
         importer.MediaImportHandler.ImportTask.UpdateType.ENTRY_CHANGED) {
       copyCount++;
@@ -595,7 +595,7 @@
 
   // Verify that the error didn't result in some files not being copied.
   reportPromise(
-      whenImportDone.then(function() {
+      whenImportDone.then(() => {
         const mockDirectoryEntry =
             /** @type {!MockDirectoryEntry} */ (destinationFileSystem.root);
         const copiedEntries = mockDirectoryEntry.getAllChildren();
@@ -690,8 +690,8 @@
   // Copy the file.
   const copyErrorCallback = /** @type {!function(FileError):*} */
       (this.errorCallback_.bind(this));
-  source.copyTo(parent, newName, function(newEntry) {
+  source.copyTo(parent, newName, newEntry => {
     this.entryChangedCallback_(source.toURL(), parent);
     this.successCallback_(newEntry);
-  }.bind(this), copyErrorCallback);
+  }, copyErrorCallback);
 };
diff --git a/ui/file_manager/file_manager/background/js/media_scanner.js b/ui/file_manager/file_manager/background/js/media_scanner.js
index d529de4..81612b5 100644
--- a/ui/file_manager/file_manager/background/js/media_scanner.js
+++ b/ui/file_manager/file_manager/background/js/media_scanner.js
@@ -25,7 +25,7 @@
    * @param {importer.ScanMode} mode Mode of the scan to find new files.
    * @return {!importer.DefaultScanResult}
    */
-  this.createScanResult_ = function(mode) {
+  this.createScanResult_ = mode => {
     return new importer.DefaultScanResult(mode, hashGenerator);
   };
 
@@ -69,11 +69,11 @@
   console.info(scan.name + ': Scanning directory ' + directory.fullPath);
 
   const watcher = this.watcherFactory_(
-      (/** @this {importer.DefaultMediaScanner} */
-      function() {
+      /** @this {importer.DefaultMediaScanner} */
+      () => {
         scan.cancel();
         this.notify_(importer.ScanEvent.INVALIDATED, scan);
-      }).bind(this));
+      });
 
   this.crawlDirectory_(directory, watcher)
       .then(this.scanMediaFiles_.bind(this, scan))
@@ -82,13 +82,12 @@
 
   scan.whenFinal()
       .then(
-          (/** @this {importer.DefaultMediaScanner} */
-          function() {
+          () => {
             console.info(
                 scan.name + ': Finished directory scan. Details: ' +
                 JSON.stringify(scan.getStatistics()));
             this.notify_(importer.ScanEvent.FINALIZED, scan);
-          }).bind(this));
+          });
 
   return scan;
 };
@@ -104,11 +103,10 @@
       entries.length + ' entries.');
 
   const watcher = this.watcherFactory_(
-      /** @this {importer.DefaultMediaScanner} */
-      (function() {
+      () => {
         scan.cancel();
         this.notify_(importer.ScanEvent.INVALIDATED, scan);
-      }).bind(this));
+      });
 
   scan.setCandidateCount(entries.length);
   const scanPromises = entries.map(this.onFileEntryFound_.bind(this, scan));
@@ -119,13 +117,12 @@
 
   scan.whenFinal()
       .then(
-          (/** @this {importer.DefaultMediaScanner} */
-          function() {
+          () => {
             console.info(
                 scan.name + ': Finished file-selection scan. Details: ' +
                 JSON.stringify(scan.getStatistics()));
             this.notify_(importer.ScanEvent.FINALIZED, scan);
-          }).bind(this));
+          });
 
   return scan;
 };
@@ -150,7 +147,7 @@
    *     to process.
    * @return {!Promise}
    */
-  const scanBatch = function(begin) {
+  const scanBatch = begin => {
     if (scan.canceled()) {
       console.debug(
           scan.name + ': Skipping remaining ' +
@@ -167,7 +164,7 @@
     return Promise.all(
         batch.map(handleFileEntry))
         .then(
-            function() {
+            () => {
               if (end < entries.length) {
                 return scanBatch(end);
               }
@@ -187,7 +184,7 @@
 importer.DefaultMediaScanner.prototype.notify_ = function(event, result) {
   this.observers_.forEach(
       /** @param {!importer.ScanObserver} observer */
-      function(observer) {
+      observer => {
         observer(event, result);
       });
 };
@@ -202,13 +199,13 @@
  * @private
  */
 importer.DefaultMediaScanner.prototype.crawlDirectory_ =
-    function(directory, watcher) {
+    (directory, watcher) => {
   const mediaFiles = [];
 
   return fileOperationUtil.findEntriesRecursively(
       directory,
       /** @param  {!Entry} entry */
-      function(entry) {
+      entry => {
         if (watcher.triggered) {
           return;
         }
@@ -223,7 +220,7 @@
         }
       })
       .then(
-          function() {
+          () => {
             return mediaFiles;
           });
 };
@@ -242,17 +239,16 @@
   return this.getDisposition_(entry, importer.Destination.GOOGLE_DRIVE,
                               scan.mode)
       .then(
-          (/**
-           * @param {!importer.Disposition} disposition The disposition
-           *     of the entry. Either some sort of dupe, or an original.
-           * @return {!Promise}
-           * @this {importer.DefaultMediaScanner}
-           */
-          function(disposition) {
+          /**
+          * @param {!importer.Disposition} disposition The disposition
+          *     of the entry. Either some sort of dupe, or an original.
+          * @return {!Promise}
+          */
+          disposition => {
             return disposition === importer.Disposition.ORIGINAL ?
                 this.onUniqueFileFound_(scan, entry) :
                 this.onDuplicateFileFound_(scan, entry, disposition);
-          }).bind(this));
+          });
 };
 
 /**
@@ -274,15 +270,14 @@
 
   return scan.addFileEntry(entry)
       .then(
-          (/**
-           * @param {boolean} added
-           * @this {importer.DefaultMediaScanner}
-           */
-          function(added) {
+          /**
+          * @param {boolean} added
+          */
+          added => {
             if (added) {
               this.notify_(importer.ScanEvent.UPDATED, scan);
             }
-          }).bind(this));
+          });
 };
 
 /**
@@ -461,22 +456,20 @@
  */
 importer.DefaultScanResult.prototype.addFileEntry = function(entry) {
   return metadataProxy.getEntryMetadata(entry).then(
-      (/**
-       * @param {!Metadata} metadata
-       * @this {importer.DefaultScanResult}
-       */
-      function(metadata) {
+      /**
+      * @param {!Metadata} metadata
+      */
+      metadata => {
         console.assert(
             'size' in metadata,
             'size attribute missing from metadata.');
 
         return this.createHashcode_(entry)
             .then(
-                (/**
-                 * @param {string} hashcode
-                 * @this {importer.DefaultScanResult}
-                 */
-                function(hashcode) {
+                /**
+                * @param {string} hashcode
+                */
+                hashcode => {
                   this.lastScanActivity_ = new Date();
 
                   if (hashcode in this.fileHashcodes_) {
@@ -491,9 +484,9 @@
                   this.fileHashcodes_[hashcode] = entry;
                   this.fileEntries_.push(entry);
                   return true;
-                }).bind(this));
+                });
 
-      }).bind(this));
+      });
 };
 
 /**
@@ -561,7 +554,7 @@
  * Registers new directory to be watched.
  * @param {!DirectoryEntry} entry
  */
-importer.DirectoryWatcher.prototype.addDirectory = function(entry) {};
+importer.DirectoryWatcher.prototype.addDirectory = entry => {};
 
 /**
  * @typedef {function()}
@@ -594,7 +587,7 @@
  *     directories is changed.
  * @return {!importer.DirectoryWatcher}
  */
-importer.DefaultDirectoryWatcher.create = function(callback) {
+importer.DefaultDirectoryWatcher.create = callback => {
   return new importer.DefaultDirectoryWatcher(callback);
 };
 
@@ -609,7 +602,7 @@
         assert(this.listener_));
   }
   this.watchedDirectories_[entry.toURL()] = true;
-  chrome.fileManagerPrivate.addFileWatch(entry, function() {});
+  chrome.fileManagerPrivate.addFileWatch(entry, () => {});
 };
 
 /**
@@ -623,12 +616,12 @@
   }
   this.triggered = true;
   for (const url in this.watchedDirectories_) {
-    window.webkitResolveLocalFileSystemURL(url, function(entry) {
+    window.webkitResolveLocalFileSystemURL(url, entry => {
       if (chrome.runtime.lastError) {
         console.error(chrome.runtime.lastError.name);
         return;
       }
-      chrome.fileManagerPrivate.removeFileWatch(entry, function() {});
+      chrome.fileManagerPrivate.removeFileWatch(entry, () => {});
     });
   }
   chrome.fileManagerPrivate.onDirectoryChanged.removeListener(
diff --git a/ui/file_manager/file_manager/background/js/media_scanner_unittest.js b/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
index 644874b..9511ecc6 100644
--- a/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
+++ b/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
@@ -37,19 +37,19 @@
 
   // Setup a default disposition checker. Tests can replace it at runtime
   // if they need specialized disposition check behavior.
-  dispositionChecker = function() {
+  dispositionChecker = () => {
     return Promise.resolve(importer.Disposition.ORIGINAL);
   };
 
   scanner = new importer.DefaultMediaScanner(
       /** @param {!FileEntry} entry */
-      function(entry) {
+      entry => {
         return Promise.resolve(entry.name);
       },
-      function(entry, destination) {
+      (entry, destination) => {
         return dispositionChecker(entry, destination);
       },
-      function(callback) {
+      callback => {
         watcher = new TestDirectoryWatcher(callback);
         return watcher;
       });
@@ -59,7 +59,7 @@
  * Verifies that scanning an empty filesystem produces an empty list.
  */
 function testEmptySourceList() {
-  assertThrows(function() {
+  assertThrows(() => {
     scanner.scanFiles([], scanMode);
   });
 }
@@ -77,7 +77,7 @@
                * Scans the directory.
                * @param {!DirectoryEntry} root
                */
-              function(root) {
+              root => {
                 const results = scanner.scanDirectory(root, scanMode);
                 assertFalse(results.isFinal());
               }),
@@ -96,20 +96,20 @@
            * Scans the directory.
            * @param {!DirectoryEntry} root
            */
-          function(root) {
+          root => {
             // Kick off a scan so we can get notified of a scan being finished.
             // We kick this off first so we can capture the result for
             // use in an assert. Promises ensure the scan won't finish
             // until after our function is fully processed.
             const result = scanner.scanDirectory(root, scanMode);
             scanner.addObserver(
-                function(eventType, scanResult) {
+                (eventType, scanResult) => {
                   assertEquals(importer.ScanEvent.FINALIZED, eventType);
                   assertEquals(result, scanResult);
                   callback(false);
                 });
           })
-      .catch(function() {
+      .catch(() => {
         callback(true);
       });
 }
@@ -135,7 +135,7 @@
           .then(fileOperationUtil.gatherEntriesRecursively)
           .then(
               /** @param {!Array<!FileEntry>} files */
-              function(files) {
+              files => {
                 return scanner.scanFiles(files, scanMode).whenFinal();
               })
           .then(assertFilesFound.bind(null, expectedFiles)),
@@ -156,7 +156,7 @@
 
   // Replace the default dispositionChecker with a function
   // that treats our dupes accordingly.
-  dispositionChecker = function(entry, destination) {
+  dispositionChecker = (entry, destination) => {
     if (entry.name === filenames[0]) {
       return Promise.resolve(importer.Disposition.HISTORY_DUPLICATE);
     }
@@ -177,7 +177,7 @@
           .then(fileOperationUtil.gatherEntriesRecursively)
           .then(
               /** @param {!Array<!FileEntry>} files */
-              function(files) {
+              files => {
                 return scanner.scanFiles(files, scanMode).whenFinal();
               })
           .then(assertFilesFound.bind(null, expectedFiles)),
@@ -200,7 +200,7 @@
                * Scans the directory.
                * @param {!DirectoryEntry} root
                */
-              function(root) {
+              root => {
                 return scanner.scanDirectory(root, scanMode).whenFinal();
               })
           .then(assertFilesFound.bind(null, [])),
@@ -232,7 +232,7 @@
                * Scans the directory.
                * @param {!DirectoryEntry} root
                */
-              function(root) {
+              root => {
                 return scanner.scanDirectory(root, scanMode).whenFinal();
               })
           .then(assertFilesFound.bind(null, expectedFiles)),
@@ -265,7 +265,7 @@
                * Scans the directory.
                * @param {!DirectoryEntry} root
                */
-              function(root) {
+              root => {
                 return scanner.scanDirectory(root, scanMode).whenFinal();
               })
           .then(assertProgress.bind(null, 100)),
@@ -289,7 +289,7 @@
 
   // Replace the default dispositionChecker with a function
   // that treats our dupes accordingly.
-  dispositionChecker = function(entry, destination) {
+  dispositionChecker = (entry, destination) => {
     if (entry.name === filenames[0]) {
       return Promise.resolve(importer.Disposition.HISTORY_DUPLICATE);
     }
@@ -313,7 +313,7 @@
                * Scans the directory.
                * @param {!DirectoryEntry} root
                */
-              function(root) {
+              root => {
                 return scanner.scanDirectory(root, scanMode).whenFinal();
               })
           .then(assertFilesFound.bind(null, expectedFiles));
@@ -335,7 +335,7 @@
 
   // Replace the default dispositionChecker with a function
   // that treats our dupes accordingly.
-  dispositionChecker = function(entry, destination) {
+  dispositionChecker = (entry, destination) => {
     if (entry.name === filenames[0]) {
       return Promise.resolve(importer.Disposition.HISTORY_DUPLICATE);
     }
@@ -361,7 +361,7 @@
                * Scans the directory.
                * @param {!DirectoryEntry} root
                */
-              function(root) {
+              root => {
                 return scanner.scanDirectory(root, scanMode).whenFinal();
               })
           .then(assertDuplicatesFound.bind(null, expectedDuplicates));
@@ -401,7 +401,7 @@
                * Scans the directory.
                * @param {!DirectoryEntry} root
                */
-              function(root) {
+              root => {
                 return scanner.scanDirectory(root, scanMode).whenFinal();
               })
           .then(assertFilesFound.bind(null, expectedFiles)),
@@ -441,7 +441,7 @@
                * Scans the directory.
                * @param {!DirectoryEntry} root
                */
-              function(root) {
+              root => {
                 return scanner.scanDirectory(root, scanMode).whenFinal();
               })
           .then(assertFilesFound.bind(null, expectedFiles)),
@@ -452,7 +452,7 @@
  * Verifies that scanning a simple single-level directory structure works.
  */
 function testDefaultScanResult() {
-  const hashGenerator = function(file) {
+  const hashGenerator = file => {
     return file.toURL();
   };
   const scan = new importer.DefaultScanResult(scanMode, hashGenerator);
@@ -472,7 +472,7 @@
 }
 
 function testInvalidation(callback) {
-  const invalidatePromise = new Promise(function(fulfill) {
+  const invalidatePromise = new Promise(fulfill => {
     scanner.addObserver(fulfill);
   });
   reportPromise(
@@ -483,7 +483,7 @@
                * Scans the directories.
                * @param {!DirectoryEntry} root
                */
-              function(root) {
+              root => {
                 scanner.scanDirectory(root, scanMode);
                 watcher.callback();
                 return invalidatePromise;
@@ -533,7 +533,7 @@
  */
 function makeTestFileSystemRoot(directoryName) {
   function makeTestFilesystem() {
-    return new Promise(function(resolve, reject) {
+    return new Promise((resolve, reject) => {
       window.webkitRequestFileSystem(
           window.TEMPORARY,
           1024 * 1024,
@@ -545,8 +545,8 @@
   return makeTestFilesystem()
       .then(
           // Create a directory, pretend that's the root.
-          function(fs) {
-            return new Promise(function(resolve, reject) {
+          fs => {
+            return new Promise((resolve, reject) => {
               fs.root.getDirectory(
                     directoryName,
                     {
@@ -570,10 +570,10 @@
 function populateDir(filenames, dir) {
   return Promise.all(
       filenames.map(
-          function(filename) {
+          filename => {
             if (filename instanceof Array) {
               return new Promise(
-                  function(resolve, reject) {
+                  (resolve, reject) => {
                     dir.getDirectory(
                         filename[0],
                         {create: true},
@@ -583,12 +583,12 @@
                   .then(populateDir.bind(null, filename));
             } else {
               const name = /** @type {string} */ (filename);
-              return new Promise(function(resolve, reject) {
+              return new Promise((resolve, reject) => {
                 dir.getFile(name, {create: true}, resolve, reject);
               });
             }
           })).then(
-              function() {
+              () => {
                 return dir;
               });
 }
diff --git a/ui/file_manager/file_manager/background/js/metadata_proxy.js b/ui/file_manager/file_manager/background/js/metadata_proxy.js
index 71bb86e..a28ee6e 100644
--- a/ui/file_manager/file_manager/background/js/metadata_proxy.js
+++ b/ui/file_manager/file_manager/background/js/metadata_proxy.js
@@ -23,13 +23,13 @@
  * @param {!FileEntry} entry
  * @return {!Promise<!Metadata>}
  */
-metadataProxy.getEntryMetadata = function(entry) {
+metadataProxy.getEntryMetadata = entry => {
   const entryURL = entry.toURL();
   if (metadataProxy.cache_.hasKey(entryURL)) {
     return Promise.resolve(metadataProxy.cache_.get(entryURL));
   } else {
-    return new Promise(function(resolve, reject) {
-      entry.getMetadata(function(metadata) {
+    return new Promise((resolve, reject) => {
+      entry.getMetadata(metadata => {
         metadataProxy.cache_.put(entryURL, metadata);
         resolve(metadata);
       }, reject);
diff --git a/ui/file_manager/file_manager/background/js/mock_drive_sync_handler.js b/ui/file_manager/file_manager/background/js/mock_drive_sync_handler.js
index d31e32e..763ecab4 100644
--- a/ui/file_manager/file_manager/background/js/mock_drive_sync_handler.js
+++ b/ui/file_manager/file_manager/background/js/mock_drive_sync_handler.js
@@ -49,7 +49,7 @@
  * Returns the completed event name.
  * @return {string}
  */
-MockDriveSyncHandler.prototype.getCompletedEventName = function() {
+MockDriveSyncHandler.prototype.getCompletedEventName = () => {
   return MockDriveSyncHandler.DRIVE_SYNC_COMPLETED_EVENT;
 };
 
diff --git a/ui/file_manager/file_manager/background/js/mock_file_operation_manager.js b/ui/file_manager/file_manager/background/js/mock_file_operation_manager.js
index 014b3de..caf9c088 100644
--- a/ui/file_manager/file_manager/background/js/mock_file_operation_manager.js
+++ b/ui/file_manager/file_manager/background/js/mock_file_operation_manager.js
@@ -73,9 +73,9 @@
     throw new Error('Only one paste call can be waited on at a time.');
   }
 
-  return new Promise(function(resolve, reject) {
+  return new Promise((resolve, reject) => {
     this.pasteResolver = resolve;
-  }.bind(this));
+  });
 };
 
 /**
@@ -96,10 +96,10 @@
   return this.generatedTaskIds.indexOf(id) !== -1;
 };
 
-MockFileOperationManager.prototype.hasQueuedTasks = function() {};
+MockFileOperationManager.prototype.hasQueuedTasks = () => {};
 
-MockFileOperationManager.prototype.filterSameDirectoryEntry = function() {};
+MockFileOperationManager.prototype.filterSameDirectoryEntry = () => {};
 
-MockFileOperationManager.prototype.deleteEntries = function() {};
+MockFileOperationManager.prototype.deleteEntries = () => {};
 
-MockFileOperationManager.prototype.zipSelection = function() {};
+MockFileOperationManager.prototype.zipSelection = () => {};
diff --git a/ui/file_manager/file_manager/background/js/mock_media_scanner.js b/ui/file_manager/file_manager/background/js/mock_media_scanner.js
index 54f9c00..d59443c4 100644
--- a/ui/file_manager/file_manager/background/js/mock_media_scanner.js
+++ b/ui/file_manager/file_manager/background/js/mock_media_scanner.js
@@ -85,7 +85,7 @@
   assertTrue(this.scans_.length > 0);
   const scan = this.scans_[this.scans_.length - 1];
   this.observers.forEach(
-      function(observer) {
+      observer => {
         observer(importer.ScanEvent.UPDATED, scan);
       });
 };
@@ -99,7 +99,7 @@
   // finalize() call before being notified to scan observers.
   /** @type {!TestScanResult} */ (scan).finalize();
 
-  this.observers.forEach(function(observer) {
+  this.observers.forEach(observer => {
     observer(importer.ScanEvent.FINALIZED, scan);
   });
 };
@@ -165,16 +165,16 @@
 
   /** @type {!Promise<!importer.ScanResult>} */
   this.whenFinal_ = new Promise(
-      function(resolve, reject) {
-        this.resolveResult_ = function(result) {
+      (resolve, reject) => {
+        this.resolveResult_ = result => {
           this.settled_ = true;
           resolve(result);
-        }.bind(this);
-        this.rejectResult_ = function() {
+        };
+        this.rejectResult_ = () => {
           this.settled_ = true;
           reject();
-        }.bind(this);
-      }.bind(this));
+        };
+      });
 }
 
 /** @private {number} */
@@ -204,12 +204,12 @@
 };
 
 /** @override */
-TestScanResult.prototype.setCandidateCount = function() {
+TestScanResult.prototype.setCandidateCount = () => {
   console.warn('setCandidateCount: not implemented');
 };
 
 /** @override */
-TestScanResult.prototype.onCandidatesProcessed = function() {
+TestScanResult.prototype.onCandidatesProcessed = () => {
   console.warn('onCandidatesProcessed: not implemented');
 };
 
@@ -266,5 +266,5 @@
 /**
  * @override
  */
-TestDirectoryWatcher.prototype.addDirectory = function() {
+TestDirectoryWatcher.prototype.addDirectory = () => {
 };
diff --git a/ui/file_manager/file_manager/background/js/mock_progress_center.js b/ui/file_manager/file_manager/background/js/mock_progress_center.js
index 3fa5c75..89f6a0a3 100644
--- a/ui/file_manager/file_manager/background/js/mock_progress_center.js
+++ b/ui/file_manager/file_manager/background/js/mock_progress_center.js
@@ -34,11 +34,11 @@
   return this.items[id];
 };
 
-MockProgressCenter.prototype.requestCancel = function() {};
+MockProgressCenter.prototype.requestCancel = () => {};
 
-MockProgressCenter.prototype.addPanel = function() {};
+MockProgressCenter.prototype.addPanel = () => {};
 
-MockProgressCenter.prototype.removePanel = function() {};
+MockProgressCenter.prototype.removePanel = () => {};
 
 /**
  * Returns the number of unique keys in |this.items|.
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 063d536..56ec174 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
@@ -31,10 +31,10 @@
  * Replaces the VolumeManager singleton with a MockVolumeManager.
  * @param {!MockVolumeManager=} opt_singleton
  */
-MockVolumeManager.installMockSingleton = function(opt_singleton) {
+MockVolumeManager.installMockSingleton = opt_singleton => {
   MockVolumeManager.instance_ = opt_singleton || new MockVolumeManager();
 
-  volumeManagerFactory.getInstance = function() {
+  volumeManagerFactory.getInstance = () => {
     return Promise.resolve(MockVolumeManager.instance_);
   };
 };
@@ -151,8 +151,7 @@
  * @param {string=} providerId Provider id.
  * @return {!VolumeInfo} Created mock VolumeInfo.
  */
-MockVolumeManager.createMockVolumeInfo = function(
-    type, volumeId, label, devicePath, providerId) {
+MockVolumeManager.createMockVolumeInfo = (type, volumeId, label, devicePath, providerId) => {
   const fileSystem = new MockFileSystem(volumeId, 'filesystem:' + volumeId);
 
   // If there's no label set it to volumeId to make it shorter to write tests.
@@ -177,23 +176,21 @@
   return volumeInfo;
 };
 
-MockVolumeManager.prototype.mountArchive = function(
-    fileUrl, successCallback, errorCallback) {
+MockVolumeManager.prototype.mountArchive = (fileUrl, successCallback, errorCallback) => {
   throw new Error('Not implemented.');
 };
-MockVolumeManager.prototype.unmount = function(
-    volumeInfo, successCallback, errorCallback) {
+MockVolumeManager.prototype.unmount = (volumeInfo, successCallback, errorCallback) => {
   throw new Error('Not implemented.');
 };
-MockVolumeManager.prototype.configure = function(volumeInfo) {
+MockVolumeManager.prototype.configure = volumeInfo => {
   throw new Error('Not implemented.');
 };
-MockVolumeManager.prototype.addEventListener = function(type, handler) {
+MockVolumeManager.prototype.addEventListener = (type, handler) => {
   throw new Error('Not implemented.');
 };
-MockVolumeManager.prototype.removeEventListener = function(type, handler) {
+MockVolumeManager.prototype.removeEventListener = (type, handler) => {
   throw new Error('Not implemented.');
 };
-MockVolumeManager.prototype.dispatchEvent = function(event) {
+MockVolumeManager.prototype.dispatchEvent = event => {
   throw new Error('Not implemented.');
 };
diff --git a/ui/file_manager/file_manager/background/js/progress_center.js b/ui/file_manager/file_manager/background/js/progress_center.js
index 54da380..58b08264 100644
--- a/ui/file_manager/file_manager/background/js/progress_center.js
+++ b/ui/file_manager/file_manager/background/js/progress_center.js
@@ -119,7 +119,7 @@
     if (item.state === ProgressItemState.CANCELED ||
         item.state === ProgressItemState.COMPLETED) {
       if (previousState === NotificationState.VISIBLE) {
-        this.queue_.run(function(proceed) {
+        this.queue_.run(proceed => {
           chrome.notifications.clear(item.id, proceed);
         });
       }
@@ -128,7 +128,7 @@
   }
 
   // Create/update the notification with the item.
-  this.queue_.run(function(proceed) {
+  this.queue_.run(proceed => {
     const params = {
       title: chrome.runtime.getManifest().name,
       iconUrl: chrome.runtime.getURL('/common/images/icon96.png'),
@@ -144,7 +144,7 @@
     } else {
       chrome.notifications.update(item.id, params, proceed);
     }
-  }.bind(this));
+  });
 };
 
 /**
@@ -158,7 +158,7 @@
 
   delete this.ids_[id];
 
-  this.queue_.run(function(proceed) {
+  this.queue_.run(proceed => {
     chrome.notifications.clear(id, proceed);
   });
 };
diff --git a/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js b/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js
index d6917ab..ae0bc02 100644
--- a/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js
+++ b/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js
@@ -69,7 +69,7 @@
  * @return {Object<{innerWidth:number, innerHeight:number}>} Map window
  *     ID and window information.
  */
-test.util.sync.getWindows = function() {
+test.util.sync.getWindows = () => {
   const windows = {};
   for (var id in window.appWindows) {
     const windowWrapper = window.appWindows[id];
@@ -93,7 +93,7 @@
  * @param {string} appId AppId of window to be closed.
  * @return {boolean} Result: True if success, false otherwise.
  */
-test.util.sync.closeWindow = function(appId) {
+test.util.sync.closeWindow = appId => {
   if (appId in window.appWindows && window.appWindows[appId].contentWindow) {
     window.appWindows[appId].close();
     return true;
@@ -105,7 +105,7 @@
  * Gets total Javascript error count from background page and each app window.
  * @return {number} Error count.
  */
-test.util.sync.getErrorCount = function() {
+test.util.sync.getErrorCount = () => {
   let totalCount = window.JSErrorCount;
   for (const appId in window.appWindows) {
     const contentWindow = window.appWindows[appId].contentWindow;
@@ -124,7 +124,7 @@
  * @param {number} height Window height.
  * @return {boolean} True for success.
  */
-test.util.sync.resizeWindow = function(contentWindow, width, height) {
+test.util.sync.resizeWindow = (contentWindow, width, height) => {
   window.appWindows[contentWindow.appID].resizeTo(width, height);
   return true;
 };
@@ -134,7 +134,7 @@
  * @param {Window} contentWindow Window to be tested.
  * @return {boolean} True for success.
  */
-test.util.sync.maximizeWindow = function(contentWindow) {
+test.util.sync.maximizeWindow = contentWindow => {
   window.appWindows[contentWindow.appID].maximize();
   return true;
 };
@@ -144,7 +144,7 @@
  * @param {Window} contentWindow Window to be tested.
  * @return {boolean} True for success.
  */
-test.util.sync.restoreWindow = function(contentWindow) {
+test.util.sync.restoreWindow = contentWindow => {
   window.appWindows[contentWindow.appID].restore();
   return true;
 };
@@ -154,7 +154,7 @@
  * @param {Window} contentWindow Window to be tested.
  * @return {boolean} True if the window is maximized now.
  */
-test.util.sync.isWindowMaximized = function(contentWindow) {
+test.util.sync.isWindowMaximized = contentWindow => {
   return window.appWindows[contentWindow.appID].isMaximized();
 };
 
@@ -170,8 +170,7 @@
  *     information that contains contentText, attribute names and
  *     values, hidden attribute, and style names and values.
  */
-test.util.sync.queryAllElements = function(
-    contentWindow, targetQuery, opt_styleNames) {
+test.util.sync.queryAllElements = (contentWindow, targetQuery, opt_styleNames) => {
   return test.util.sync.deepQueryAllElements(
       contentWindow, targetQuery, opt_styleNames);
 };
@@ -190,8 +189,7 @@
  *     information that contains contentText, attribute names and
  *     values, hidden attribute, and style names and values.
  */
-test.util.sync.deepQueryAllElements = function(
-    contentWindow, targetQuery, opt_styleNames) {
+test.util.sync.deepQueryAllElements = (contentWindow, targetQuery, opt_styleNames) => {
   if (!contentWindow.document) {
     return [];
   }
@@ -201,7 +199,7 @@
 
   const elems =
       test.util.sync.deepQuerySelectorAll_(contentWindow.document, targetQuery);
-  return elems.map(function(element) {
+  return elems.map(element => {
     return extractElementInfo(element, contentWindow, opt_styleNames);
   });
 };
@@ -217,7 +215,7 @@
  *
  * @private
  */
-test.util.sync.deepQuerySelectorAll_ = function(root, targetQuery) {
+test.util.sync.deepQuerySelectorAll_ = (root, targetQuery) => {
   const elems = Array.prototype.slice.call(root.querySelectorAll(targetQuery[0]));
   const remaining = targetQuery.slice(1);
   if (remaining.length === 0) {
@@ -248,8 +246,7 @@
  * @param {function(*)} callback Callback function to be called with the
  *   result of the |script|.
  */
-test.util.async.deepExecuteScriptInWebView = function(
-    contentWindow, targetQuery, script, callback) {
+test.util.async.deepExecuteScriptInWebView = (contentWindow, targetQuery, script, callback) => {
   const webviews =
       test.util.sync.deepQuerySelectorAll_(contentWindow.document, targetQuery);
   if (!webviews || webviews.length !== 1) {
@@ -271,7 +268,7 @@
  *     values, hidden attribute, and style names and values. If there is no
  *     active element, returns null.
  */
-test.util.sync.getActiveElement = function(contentWindow, opt_styleNames) {
+test.util.sync.getActiveElement = (contentWindow, opt_styleNames) => {
   if (!contentWindow.document || !contentWindow.document.activeElement) {
     return null;
   }
@@ -286,7 +283,7 @@
  * @param {string} query Query for the input element.
  * @param {string} text Text to be assigned.
  */
-test.util.sync.inputText = function(contentWindow, query, text) {
+test.util.sync.inputText = (contentWindow, query, text) => {
   const input = contentWindow.document.querySelector(query);
   input.value = text;
 };
@@ -304,7 +301,7 @@
  * @param {!Event} event Event to be sent.
  * @return {boolean} True if the event is sent to the target, false otherwise.
  */
-test.util.sync.sendEvent = function(contentWindow, targetQuery, event) {
+test.util.sync.sendEvent = (contentWindow, targetQuery, event) => {
   if (!contentWindow.document) {
     return false;
   }
@@ -340,8 +337,7 @@
  *     properties.
  * @return {boolean} True if the event is sent to the target, false otherwise.
  */
-test.util.sync.fakeEvent = function(
-    contentWindow, targetQuery, eventType, opt_additionalProperties) {
+test.util.sync.fakeEvent = (contentWindow, targetQuery, eventType, opt_additionalProperties) => {
   const event = new Event(
       eventType,
       /** @type {!EventInit} */ (opt_additionalProperties || {}));
@@ -366,8 +362,7 @@
  * @param {boolean} alt whether ALT should be pressed, or not.
  * @return {boolean} True if the event is sent to the target, false otherwise.
  */
-test.util.sync.fakeKeyDown = function(
-    contentWindow, targetQuery, key, ctrl, shift, alt) {
+test.util.sync.fakeKeyDown = (contentWindow, targetQuery, key, ctrl, shift, alt) => {
   const event = new KeyboardEvent('keydown', {
     bubbles: true,
     composed: true,  // Allow the event to bubble past shadow DOM root.
@@ -395,8 +390,7 @@
  * @return {boolean} True if the all events are sent to the target, false
  *     otherwise.
  */
-test.util.sync.fakeMouseClick = function(
-    contentWindow, targetQuery, opt_keyModifiers) {
+test.util.sync.fakeMouseClick = (contentWindow, targetQuery, opt_keyModifiers) => {
   const modifiers = opt_keyModifiers || {};
   const props = {
     bubbles: true,
@@ -430,7 +424,7 @@
  * @return {boolean} True if the event is sent to the target, false
  *     otherwise.
  */
-test.util.sync.fakeMouseRightClick = function(contentWindow, targetQuery) {
+test.util.sync.fakeMouseRightClick = (contentWindow, targetQuery) => {
   const mouseDownEvent =
       new MouseEvent('mousedown', {bubbles: true, button: 2, composed: true});
   if (!test.util.sync.sendEvent(contentWindow, targetQuery, mouseDownEvent)) {
@@ -451,7 +445,7 @@
  * @return {boolean} True if the event is sent to the target, false
  *     otherwise.
  */
-test.util.sync.fakeTouchClick = function(contentWindow, targetQuery) {
+test.util.sync.fakeTouchClick = (contentWindow, targetQuery) => {
   const touchStartEvent = new TouchEvent('touchstart');
   if (!test.util.sync.sendEvent(contentWindow, targetQuery, touchStartEvent)) {
     return false;
@@ -481,7 +475,7 @@
  * @param {string} targetQuery Query to specify the element.
  * @return {boolean} True if the event is sent to the target, false otherwise.
  */
-test.util.sync.fakeMouseDoubleClick = function(contentWindow, targetQuery) {
+test.util.sync.fakeMouseDoubleClick = (contentWindow, targetQuery) => {
   // Double click is always preceded with a single click.
   if (!test.util.sync.fakeMouseClick(contentWindow, targetQuery)) {
     return false;
@@ -511,7 +505,7 @@
  * @param {string} targetQuery Query to specify the element.
  * @return {boolean} True if the event is sent to the target, false otherwise.
  */
-test.util.sync.fakeMouseDown = function(contentWindow, targetQuery) {
+test.util.sync.fakeMouseDown = (contentWindow, targetQuery) => {
   const event = new MouseEvent('mousedown', {bubbles: true, composed: true});
   return test.util.sync.sendEvent(contentWindow, targetQuery, event);
 };
@@ -523,7 +517,7 @@
  * @param {string} targetQuery Query to specify the element.
  * @return {boolean} True if the event is sent to the target, false otherwise.
  */
-test.util.sync.fakeMouseUp = function(contentWindow, targetQuery) {
+test.util.sync.fakeMouseUp = (contentWindow, targetQuery) => {
   const event = new MouseEvent('mouseup', {bubbles: true, composed: true});
   return test.util.sync.sendEvent(contentWindow, targetQuery, event);
 };
@@ -537,7 +531,7 @@
  * @return {boolean} True if focus method of the element has been called, false
  *     otherwise.
  */
-test.util.sync.focus = function(contentWindow, targetQuery) {
+test.util.sync.focus = (contentWindow, targetQuery) => {
   const target = contentWindow.document &&
       contentWindow.document.querySelector(targetQuery);
 
@@ -554,7 +548,7 @@
  * @param {function(Object<boolean>)} callback Callback function with
  *     results returned by the script.
  */
-test.util.async.getNotificationIDs = function(callback) {
+test.util.async.getNotificationIDs = callback => {
   chrome.notifications.getAll(callback);
 };
 
@@ -570,7 +564,7 @@
  * "FileBrowserBackground" doesn't define the attributes "launcherSearch_" so we
  * need to suppress missingProperties.
  */
-test.util.sync.launcherSearchOpenResult = function(fileURL) {
+test.util.sync.launcherSearchOpenResult = fileURL => {
   window.background.launcherSearch_.onOpenResult_(fileURL);
 };
 
@@ -582,15 +576,15 @@
  * @param {function(*)} callback Callback function with results returned by the
  *     script.
  */
-test.util.async.getFilesUnderVolume = function(volumeType, names, callback) {
+test.util.async.getFilesUnderVolume = (volumeType, names, callback) => {
   const displayRootPromise =
-      volumeManagerFactory.getInstance().then(function(volumeManager) {
+      volumeManagerFactory.getInstance().then(volumeManager => {
         const volumeInfo = volumeManager.getCurrentProfileVolumeInfo(volumeType);
         return volumeInfo.resolveDisplayRoot();
       });
 
-  const retrievePromise = displayRootPromise.then(function(displayRoot) {
-    const filesPromise = names.map(function(name) {
+  const retrievePromise = displayRootPromise.then(displayRoot => {
+    const filesPromise = names.map(name => {
       // TODO(crbug.com/880130): Remove this conditional.
       if (volumeType === VolumeManagerCommon.VolumeType.DOWNLOADS &&
           util.isMyFilesVolumeEnabled()) {
@@ -599,10 +593,10 @@
       return new Promise(displayRoot.getFile.bind(displayRoot, name, {}));
     });
     return Promise.all(filesPromise)
-        .then(function(aa) {
+        .then(aa => {
           return util.entriesToURLs(aa);
         })
-        .catch(function() {
+        .catch(() => {
           return [];
         });
   });
@@ -616,7 +610,7 @@
  * @param {VolumeManagerCommon.VolumeType} volumeType Volume type.
  * @param {function(boolean)} callback Function receives true on success.
  */
-test.util.async.unmount = function(volumeType, callback) {
+test.util.async.unmount = (volumeType, callback) => {
   volumeManagerFactory.getInstance().then((volumeManager) => {
     const volumeInfo = volumeManager.getCurrentProfileVolumeInfo(volumeType);
     if (volumeInfo) {
@@ -632,7 +626,7 @@
  * @param {*} request
  * @param {function(*):void} sendResponse
  */
-test.util.executeTestMessage = function(request, sendResponse) {
+test.util.executeTestMessage = (request, sendResponse) => {
   window.IN_TEST = true;
   // Check the function name.
   if (!request.func || request.func[request.func.length - 1] == '_') {
@@ -656,9 +650,9 @@
   }
   // Call the test utility function and respond the result.
   if (test.util.async[request.func]) {
-    args[test.util.async[request.func].length - 1] = function() {
+    args[test.util.async[request.func].length - 1] = function(...innerArgs) {
       console.debug('Received the result of ' + request.func);
-      sendResponse.apply(null, arguments);
+      sendResponse.apply(null, innerArgs);
     };
     console.debug('Waiting for the result of ' + request.func);
     test.util.async[request.func].apply(null, args);
@@ -680,7 +674,7 @@
  *   Window so it isn't visible in the background. Here it will return as JSON
  *   object to test extension.
  */
-test.util.sync.getMetadataStats = function(contentWindow) {
+test.util.sync.getMetadataStats = contentWindow => {
   return contentWindow.fileManager.metadataModel.getStats();
 };
 
@@ -688,7 +682,7 @@
  * Returns true when FileManager has finished loading, by checking the attribute
  * "loaded" on its root element.
  */
-test.util.sync.isFileManagerLoaded = function(contentWindow) {
+test.util.sync.isFileManagerLoaded = contentWindow => {
   if (contentWindow && contentWindow.fileManager &&
       contentWindow.fileManager.ui) {
     return contentWindow.fileManager.ui.element.hasAttribute('loaded');
@@ -702,7 +696,7 @@
  *
  * @return {Array<string>}
  */
-test.util.sync.getA11yAnnounces = function(contentWindow) {
+test.util.sync.getA11yAnnounces = contentWindow => {
   if (contentWindow && contentWindow.fileManager &&
       contentWindow.fileManager.ui) {
     return contentWindow.fileManager.ui.a11yAnnounces;
@@ -718,7 +712,7 @@
  * @param {function(number)} callback Callback function to be called with the
  *   number of volumes.
  */
-test.util.async.getVolumesCount = function(callback) {
+test.util.async.getVolumesCount = callback => {
   return volumeManagerFactory.getInstance().then((volumeManager) => {
     callback(volumeManager.volumeInfoList.length);
   });
diff --git a/ui/file_manager/file_manager/background/js/task_queue.js b/ui/file_manager/file_manager/background/js/task_queue.js
index 7c52c00..da0c33c 100644
--- a/ui/file_manager/file_manager/background/js/task_queue.js
+++ b/ui/file_manager/file_manager/background/js/task_queue.js
@@ -52,14 +52,14 @@
 importer.TaskQueue.prototype.queueTask = function(task) {
   // The Tasks that are pushed onto the queue aren't required to be inherently
   // asynchronous.  This code force task execution to occur asynchronously.
-  Promise.resolve().then(function() {
+  Promise.resolve().then(() => {
     task.addObserver(this.onTaskUpdate_.bind(this, task));
     this.tasks_.push(task);
     // If more than one task is queued, then the queue is already running.
     if (this.tasks_.length === 1) {
       this.runPending_();
     }
-  }.bind(this));
+  });
 };
 
 /**
@@ -98,7 +98,7 @@
  */
 importer.TaskQueue.prototype.onTaskUpdate_ = function(task, updateType) {
   // Send a task update to clients.
-  this.updateCallbacks_.forEach(function(callback) {
+  this.updateCallbacks_.forEach(callback => {
     callback.call(null, updateType, task);
   });
 
@@ -213,10 +213,10 @@
 };
 
 /** @override */
-importer.TaskQueue.BaseTask.prototype.run = function() {};
+importer.TaskQueue.BaseTask.prototype.run = () => {};
 
 /**
- * @param {string} updateType
+ * @param {importer.TaskQueue.UpdateType} updateType
  * @param {Object=} opt_data
  * @protected
  */
@@ -228,7 +228,7 @@
   }
 
   this.observers_.forEach(
-      function(callback) {
+      callback => {
         callback.call(null, updateType, opt_data);
-      }.bind(this));
+      });
 };
diff --git a/ui/file_manager/file_manager/background/js/task_queue_unittest.js b/ui/file_manager/file_manager/background/js/task_queue_unittest.js
index c118d05..fd5cf32 100644
--- a/ui/file_manager/file_manager/background/js/task_queue_unittest.js
+++ b/ui/file_manager/file_manager/background/js/task_queue_unittest.js
@@ -18,7 +18,7 @@
   }
 
   // Counts the number of updates of each type that have been received.
-  const updateCallback = function(type, updatedTask) {
+  const updateCallback = (type, updatedTask) => {
     updates[type]++;
   };
   queue.addUpdateCallback(updateCallback);
@@ -42,9 +42,9 @@
    */
   this.runResolver_ = null;
 
-  this.runPromise_ = new Promise(function(resolve) {
+  this.runPromise_ = new Promise(resolve => {
     this.runResolver_ = resolve;
-  }.bind(this));
+  });
 };
 TestTask.prototype.__proto__ = importer.TaskQueue.BaseTask.prototype;
 
@@ -92,10 +92,10 @@
   const task1 = new TestTask('task1');
 
   // Make the tasks call Task#notifyComplete when they are run.
-  task0.whenRun().then(function(task) {
+  task0.whenRun().then(task => {
     task.notifyComplete();
   });
-  task1.whenRun().then(function(task) {
+  task1.whenRun().then(task => {
     task.notifyComplete();
   });
 
@@ -112,9 +112,9 @@
   const task = new TestTask('task0');
 
   // Make a promise that resolves when the active callback is triggered.
-  const whenActive = new Promise(function(resolve) {
+  const whenActive = new Promise(resolve => {
     queue.setActiveCallback(
-        function() {
+        () => {
           // Verify that the active callback is called before the task runs.
           assertFalse(task.wasRun);
           resolve();
@@ -130,15 +130,15 @@
 function testOnIdleCalled(callback) {
   const task = new TestTask('task0');
 
-  task.whenRun().then(function(task) {
+  task.whenRun().then(task => {
     task.notifyComplete();
   });
 
   // Make a promise that resolves when the idle callback is triggered
   // (i.e. after all queued tasks have finished running).
-  const whenDone = new Promise(function(resolve) {
+  const whenDone = new Promise(resolve => {
     queue.setIdleCallback(
-        function() {
+        () => {
           // Verify that the idle callback is called after the task runs.
           assertTrue(task.wasRun);
           resolve();
@@ -157,20 +157,20 @@
   // Get the task to report some progress, then success, when it's run.
   task.whenRun()
       .then(
-          function(task) {
+          task => {
             task.notifyProgress();
             return task;
           })
       .then(
-          function(task) {
+          task => {
             task.notifyComplete();
             return task;
           });
 
   // Make a promise that resolves after the task runs.
-  const whenDone = new Promise(function(resolve) {
+  const whenDone = new Promise(resolve => {
     queue.setIdleCallback(
-        function() {
+        () => {
           // Verify that progress was recorded.
           assertEquals(1, updates[importer.TaskQueue.UpdateType.PROGRESS]);
           resolve();
@@ -187,15 +187,15 @@
   const task = new TestTask('task0');
 
   // Get the task to report success when it's run.
-  task.whenRun().then(function(task) {
+  task.whenRun().then(task => {
     task.notifyComplete();
   });
 
   queue.queueTask(task);
 
-  const whenDone = new Promise(function(resolve) {
+  const whenDone = new Promise(resolve => {
     queue.setIdleCallback(
-        function() {
+        () => {
           // Verify that the done callback was called.
           assertEquals(1, updates[importer.TaskQueue.UpdateType.COMPLETE]);
           resolve();
@@ -211,7 +211,7 @@
 
   // Get the task to report an error when it's run.
   task.whenRun().then(
-      function(task) {
+      task => {
         task.notifyError();
         // Errors are not terminal; still need to signal task completion
         // otherwise the test hangs.
@@ -220,9 +220,9 @@
 
   queue.queueTask(task);
 
-  const whenDone = new Promise(function(resolve) {
+  const whenDone = new Promise(resolve => {
     queue.setIdleCallback(
-        function() {
+        () => {
           // Verify that the done callback was called.
           assertEquals(1, updates[importer.TaskQueue.UpdateType.ERROR]);
           resolve();
@@ -237,10 +237,10 @@
   const task1 = new TestTask('task1');
 
   // Make the tasks call Task#notifyComplete when they are run.
-  task0.whenRun().then(function(task) {
+  task0.whenRun().then(task => {
     task.notifyCanceled();
   });
-  task1.whenRun().then(function(task) {
+  task1.whenRun().then(task => {
     task.notifyComplete();
   });
 
diff --git a/ui/file_manager/file_manager/background/js/test_import_history.js b/ui/file_manager/file_manager/background/js/test_import_history.js
index 4184cb2..ef69fa7 100644
--- a/ui/file_manager/file_manager/background/js/test_import_history.js
+++ b/ui/file_manager/file_manager/background/js/test_import_history.js
@@ -99,7 +99,7 @@
 
 /** @override */
 importer.TestImportHistory.prototype.listUnimportedUrls =
-    function(destination) {
+    destination => {
   return Promise.resolve([]);
 };
 
@@ -145,13 +145,13 @@
 };
 
 /** @override */
-importer.TestImportHistory.prototype.whenReady = function() {};
+importer.TestImportHistory.prototype.whenReady = () => {};
 
 /** @override */
-importer.TestImportHistory.prototype.markImportedByUrl = function() {};
+importer.TestImportHistory.prototype.markImportedByUrl = () => {};
 
 /** @override */
-importer.TestImportHistory.prototype.addObserver = function() {};
+importer.TestImportHistory.prototype.addObserver = () => {};
 
 /** @override */
-importer.TestImportHistory.prototype.removeObserver = function() {};
+importer.TestImportHistory.prototype.removeObserver = () => {};
diff --git a/ui/file_manager/file_manager/background/js/test_util.js b/ui/file_manager/file_manager/background/js/test_util.js
index 0c148b1..7389ae4 100644
--- a/ui/file_manager/file_manager/background/js/test_util.js
+++ b/ui/file_manager/file_manager/background/js/test_util.js
@@ -9,7 +9,7 @@
  * @param {function(string)} callback Completion callback with the new window's
  *     App ID.
  */
-test.util.async.openMainWindow = function(appState, callback) {
+test.util.async.openMainWindow = (appState, callback) => {
   launcher.launchFileManager(appState,
                     undefined,  // opt_type
                     undefined,  // opt_id
@@ -23,7 +23,7 @@
  * @param {Window} contentWindow Window to be tested.
  * @return {string} Name of selected tree item.
  */
-test.util.sync.getSelectedTreeItem = function(contentWindow) {
+test.util.sync.getSelectedTreeItem = contentWindow => {
   const tree = contentWindow.document.querySelector('#directory-tree');
   const items = tree.querySelectorAll('.tree-item');
   const selected = [];
@@ -47,7 +47,7 @@
  * @param {Window} contentWindow Window to be tested.
  * @return {Array<Array<string>>} Details for each visible file row.
  */
-test.util.sync.getFileList = function(contentWindow) {
+test.util.sync.getFileList = contentWindow => {
   const table = contentWindow.document.querySelector('#detail-table');
   const rows = table.querySelectorAll('li');
   const fileList = [];
@@ -70,7 +70,7 @@
  * @param {Window} contentWindow Window to be tested.
  * @return {Array<string>} Selected file names.
  */
-test.util.sync.getSelectedFiles = function(contentWindow) {
+test.util.sync.getSelectedFiles = contentWindow => {
   const table = contentWindow.document.querySelector('#detail-table');
   const rows = table.querySelectorAll('li');
   const selected = [];
@@ -89,7 +89,7 @@
  * @param {string} filename Name of the file to be selected.
  * @return {boolean} True if file got selected, false otherwise.
  */
-test.util.sync.selectFile = function(contentWindow, filename) {
+test.util.sync.selectFile = (contentWindow, filename) => {
   const rows = contentWindow.document.querySelectorAll('#detail-table li');
   test.util.sync.focus(contentWindow, '#file-list');
   test.util.sync.fakeKeyDown(
@@ -114,7 +114,7 @@
  * @return {boolean} True if file got selected and a double click message is
  *     sent, false otherwise.
  */
-test.util.sync.openFile = function(contentWindow, filename) {
+test.util.sync.openFile = (contentWindow, filename) => {
   const query = '#file-list li.table-row[selected] .filename-label span';
   return test.util.sync.selectFile(contentWindow, filename) &&
          test.util.sync.fakeMouseDoubleClick(contentWindow, query);
@@ -128,7 +128,7 @@
  * @param {function(boolean)} callback Callback function to notify the caller
  *     whether the target is found and mousedown and click events are sent.
  */
-test.util.async.selectVolume = function(contentWindow, iconName, callback) {
+test.util.async.selectVolume = (contentWindow, iconName, callback) => {
   const query = '#directory-tree [volume-type-icon=' + iconName + ']';
   const isDriveSubVolume = iconName == 'drive_recent' ||
       iconName == 'drive_shared_with_me' || iconName == 'drive_offline';
@@ -147,8 +147,7 @@
  * @param {function(boolean)} callback Callback function to notify the caller
  *     whether the target is found and mousedown and click events are sent.
  */
-test.util.async.selectInDirectoryTree = function(
-    contentWindow, query, isDriveSubVolume, callback) {
+test.util.async.selectInDirectoryTree = (contentWindow, query, isDriveSubVolume, callback) => {
   const driveQuery = '#directory-tree [volume-type-icon=drive]';
   let preSelection = false;
   const steps = {
@@ -191,7 +190,7 @@
  * @param {string} folderName Name of the folder to be selected.
  * @return {boolean} True if file got selected, false otherwise.
  */
-test.util.sync.selectFolderInTree = function(contentWindow, folderName) {
+test.util.sync.selectFolderInTree = (contentWindow, folderName) => {
   const items =
       contentWindow.document.querySelectorAll('#directory-tree .tree-item');
   test.util.sync.fakeKeyDown(
@@ -219,7 +218,7 @@
  * @param {Window} contentWindow Window to be tested.
  * @return {boolean} True if folder got expanded, false otherwise.
  */
-test.util.sync.expandSelectedFolderInTree = function(contentWindow) {
+test.util.sync.expandSelectedFolderInTree = contentWindow => {
   const selectedItem = contentWindow.document.querySelector(
       '#directory-tree .tree-item[selected]');
   if (!selectedItem) {
@@ -239,7 +238,7 @@
  * @param {Window} contentWindow Window to be tested.
  * @return {boolean} True if folder got expanded, false otherwise.
  */
-test.util.sync.collapseSelectedFolderInTree = function(contentWindow) {
+test.util.sync.collapseSelectedFolderInTree = contentWindow => {
   const selectedItem = contentWindow.document.querySelector(
       '#directory-tree .tree-item[selected]');
   if (!selectedItem) {
@@ -262,7 +261,7 @@
  * @param {string} folderName Name of the folder to be selected.
  * @return {boolean} True if Team Drive folder got selected, false otherwise.
  */
-test.util.sync.selectTeamDrive = function(contentWindow, teamDriveName) {
+test.util.sync.selectTeamDrive = (contentWindow, teamDriveName) => {
   // Select + expand Team Drives gran root.
   const teamDrivesSelector = '#directory-tree .tree-item ' +
       '[entry-label="Team Drives"]:not([hidden])';
@@ -292,7 +291,7 @@
  * @param {Window} contentWindow Window to be tested.
  * @return {!Array<string>} List of visible item names.
  */
-test.util.sync.getTreeItems = function(contentWindow) {
+test.util.sync.getTreeItems = contentWindow => {
   const items = contentWindow.document.querySelectorAll(
       '#directory-tree .tree-item');
   const result = [];
@@ -311,7 +310,7 @@
  * @param {Window} contentWindow The window where visitURL() was called.
  * @return {!string} The URL of the last URL visited.
  */
-test.util.sync.getLastVisitedURL = function(contentWindow) {
+test.util.sync.getLastVisitedURL = contentWindow => {
   return contentWindow.util.getLastVisitedURL();
 };
 
@@ -324,8 +323,7 @@
  * @param {function(*)} callback Callback function with results returned by the
  *     script.
  */
-test.util.async.executeScriptInWebView = function(
-    contentWindow, webViewQuery, code, callback) {
+test.util.async.executeScriptInWebView = (contentWindow, webViewQuery, code, callback) => {
   const webView = contentWindow.document.querySelector(webViewQuery);
   webView.executeScript({code: code}, callback);
 };
@@ -338,7 +336,7 @@
  * @return {boolean} True if copying got simulated successfully. It does not
  *     say if the file got copied, or not.
  */
-test.util.sync.copyFile = function(contentWindow, filename) {
+test.util.sync.copyFile = (contentWindow, filename) => {
   if (!test.util.sync.selectFile(contentWindow, filename)) {
     return false;
   }
@@ -358,7 +356,7 @@
  * @return {boolean} True if deleting got simulated successfully. It does not
  *     say if the file got deleted, or not.
  */
-test.util.sync.deleteFile = function(contentWindow, filename) {
+test.util.sync.deleteFile = (contentWindow, filename) => {
   if (!test.util.sync.selectFile(contentWindow, filename)) {
     return false;
   }
@@ -375,7 +373,7 @@
  * @param {string} command Command name.
  * @return {boolean} True if the command is executed successfully.
  */
-test.util.sync.execCommand = function(contentWindow, command) {
+test.util.sync.execCommand = (contentWindow, command) => {
   return contentWindow.document.execCommand(command);
 };
 
@@ -389,14 +387,14 @@
  * @return {boolean} Always return true.
  */
 test.util.sync.overrideInstallWebstoreItemApi =
-    function(contentWindow, expectedItemId, intendedError) {
-  const setLastError = function(message) {
+    (contentWindow, expectedItemId, intendedError) => {
+  const setLastError = message => {
     contentWindow.chrome.runtime.lastError =
         message ? {message: message} : undefined;
   };
 
-  const installWebstoreItem = function(itemId, silentInstallation, callback) {
-    setTimeout(function() {
+  const installWebstoreItem = (itemId, silentInstallation, callback) => {
+    setTimeout(() => {
       if (itemId !== expectedItemId) {
         setLastError('Invalid Chrome Web Store item ID');
         callback();
@@ -422,19 +420,19 @@
  *     fileManagerPrivate.getFileTasks().
  * @return {boolean} Always return true.
  */
-test.util.sync.overrideTasks = function(contentWindow, taskList) {
-  const getFileTasks = function(entries, onTasks) {
+test.util.sync.overrideTasks = (contentWindow, taskList) => {
+  const getFileTasks = (entries, onTasks) => {
     // Call onTask asynchronously (same with original getFileTasks).
-    setTimeout(function() {
+    setTimeout(() => {
       onTasks(taskList);
     }, 0);
   };
 
-  const executeTask = function(taskId, entry) {
+  const executeTask = (taskId, entry) => {
     test.util.executedTasks_.push(taskId);
   };
 
-  const setDefaultTask = function(taskId) {
+  const setDefaultTask = taskId => {
     for (let i = 0; i < taskList.length; i++) {
       taskList[i].isDefault = taskList[i].taskId === taskId;
     }
@@ -452,7 +450,7 @@
  * @param {Window} contentWindow Window to be tested.
  * @return {Array<string>} List of executed task ID.
  */
-test.util.sync.getExecutedTasks = function(contentWindow) {
+test.util.sync.getExecutedTasks = contentWindow => {
   if (!test.util.executedTasks_) {
     console.error('Please call overrideTasks() first.');
     return null;
@@ -467,7 +465,7 @@
  * @param {string} profileId Destination profile's ID.
  * @return {boolean} True if the menu is found and run.
  */
-test.util.sync.runVisitDesktopMenu = function(contentWindow, profileId) {
+test.util.sync.runVisitDesktopMenu = (contentWindow, profileId) => {
   const list = contentWindow.document.querySelectorAll('.visit-desktop');
   for (let i = 0; i < list.length; ++i) {
     if (list[i].label.indexOf(profileId) != -1) {
@@ -484,7 +482,7 @@
  * Calls the unload handler for the window.
  * @param {Window} contentWindow Window to be tested.
  */
-test.util.sync.unload = function(contentWindow) {
+test.util.sync.unload = contentWindow => {
   contentWindow.fileManager.onUnload_();
 };
 
@@ -494,7 +492,7 @@
  * @param {Window} contentWindow Window to be tested.
  * @return {string} Path which is shown in the breadcrumb.
  */
-test.util.sync.getBreadcrumbPath = function(contentWindow) {
+test.util.sync.getBreadcrumbPath = contentWindow => {
   const breadcrumb = contentWindow.document.querySelector(
       '#location-breadcrumbs');
   const paths = breadcrumb.querySelectorAll('.breadcrumb-path');
@@ -511,7 +509,7 @@
  * @param {function(Object)} callback Callback function with results returned by
  *     the script.
  */
-test.util.async.getPreferences = function(callback) {
+test.util.async.getPreferences = callback => {
   chrome.fileManagerPrivate.getPreferences(callback);
 };
 
diff --git a/ui/file_manager/file_manager/background/js/test_util_base.js b/ui/file_manager/file_manager/background/js/test_util_base.js
index 09ee68a8..a6b0777f 100644
--- a/ui/file_manager/file_manager/background/js/test_util_base.js
+++ b/ui/file_manager/file_manager/background/js/test_util_base.js
@@ -39,13 +39,12 @@
 /**
  * Registers message listener, which runs test utility functions.
  */
-test.util.registerRemoteTestUtils = function() {
+test.util.registerRemoteTestUtils = () => {
   let responsesWaitingForLoad = [];
 
   // Return true for asynchronous functions, which keeps the connection to the
   // caller alive; Return false for synchronous functions.
-  chrome.runtime.onMessageExternal.addListener(function(
-      request, sender, sendResponse) {
+  chrome.runtime.onMessageExternal.addListener((request, sender, sendResponse) => {
     /**
      * List of extension ID of the testing extension.
      * @type {Array<string>}
@@ -87,7 +86,7 @@
     let script = document.createElement('script');
     document.body.appendChild(script);
 
-    script.onload = function() {
+    script.onload = () => {
       // Run queued request/response pairs.
       responsesWaitingForLoad.forEach((queueObj) => {
         test.util.executeTestMessage(queueObj.request, queueObj.sendResponse);
@@ -95,7 +94,7 @@
       responsesWaitingForLoad = [];
     };
 
-    script.onerror = function(/** Event */ event) {
+    script.onerror = /** Event */ event => {
       console.error('Failed to load the run-time test script: ' + event);
       throw new Error('Failed to load the run-time test script: ' + event);
     };
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 dd40150e5..fab47c8 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
@@ -253,7 +253,7 @@
  * @param {string} url The filesystem URL
  * @return {!Promise<Entry>}
  */
-VolumeInfoImpl.resolveFileSystemUrl_ = function(url) {
+VolumeInfoImpl.resolveFileSystemUrl_ = url => {
   return new Promise(window.webkitResolveLocalFileSystemURL.bind(null, url));
 };
 
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_factory.js b/ui/file_manager/file_manager/background/js/volume_manager_factory.js
index c63994e..019f30a6 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_factory.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_factory.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-var volumeManagerFactory = (function() {
+var volumeManagerFactory = (() => {
   /**
    * The singleton instance of VolumeManager. Initialized by the first
    * invocation of getInstance().
@@ -27,8 +27,8 @@
   function getInstance(opt_callback) {
     if (!instancePromise) {
       instance = new VolumeManagerImpl();
-      instancePromise = new Promise(function(fulfill) {
-        instance.initialize_(function() {
+      instancePromise = new Promise(fulfill => {
+        instance.initialize_(() => {
           return fulfill(instance);
         });
       });
@@ -62,4 +62,4 @@
     getInstanceForDebug: getInstanceForDebug,
     revokeInstanceForTesting: revokeInstanceForTesting
   };
-}());
+})();
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 5fa1c18..832e6cf 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
@@ -51,10 +51,12 @@
  * @private
  */
 VolumeManagerImpl.prototype.onDriveConnectionStatusChanged_ = function() {
-  chrome.fileManagerPrivate.getDriveConnectionState(function(state) {
-    this.driveConnectionState_ = state;
+  chrome.fileManagerPrivate.getDriveConnectionState(state => {
+    // TODO(crbug.com/931971): Convert private API to use enum.
+    this.driveConnectionState_ =
+        /** @type {VolumeManagerCommon.DriveConnectionState} */ (state);
     cr.dispatchSimpleEvent(this, 'drive-connection-changed');
-  }.bind(this));
+  });
 };
 
 /** @override */
@@ -76,11 +78,11 @@
  */
 VolumeManagerImpl.prototype.addVolumeMetadata_ = function(volumeMetadata) {
   return volumeManagerUtil.createVolumeInfo(volumeMetadata).then(
-      (/**
-       * @param {!VolumeInfo} volumeInfo
-       * @return {!VolumeInfo}
-       */
-      function(volumeInfo) {
+      /**
+      * @param {!VolumeInfo} volumeInfo
+      * @return {!VolumeInfo}
+      */
+      volumeInfo => {
         // We don't show Downloads and Drive on volume list if they have mount
         // error, since users can do nothing in this situation.
         // We show Removable and Provided volumes regardless of mount error so
@@ -116,7 +118,7 @@
           this.volumeInfoList.add(volumeInfo);
         }
         return volumeInfo;
-      }).bind(this));
+      });
 };
 
 /**
@@ -129,25 +131,25 @@
   chrome.fileManagerPrivate.onMountCompleted.addListener(
       this.onMountCompleted_.bind(this));
   console.debug('Requesting volume list.');
-  chrome.fileManagerPrivate.getVolumeMetadataList(function(volumeMetadataList) {
+  chrome.fileManagerPrivate.getVolumeMetadataList(volumeMetadataList => {
     console.debug(
         'Volume list fetched with: ' + volumeMetadataList.length + ' items.');
     // We must subscribe to the mount completed event in the callback of
     // getVolumeMetadataList. crbug.com/330061.
     // But volumes reported by onMountCompleted events must be added after the
     // volumes in the volumeMetadataList are mounted. crbug.com/135477.
-    this.mountQueue_.run(function(inCallback) {
+    this.mountQueue_.run(inCallback => {
       // Create VolumeInfo for each volume.
       Promise.all(
-          volumeMetadataList.map(function(volumeMetadata) {
+          volumeMetadataList.map(volumeMetadata => {
             console.debug(
                 'Initializing volume: ' + volumeMetadata.volumeId);
             return this.addVolumeMetadata_(volumeMetadata).then(
-                function(volumeInfo) {
+                volumeInfo => {
                   console.debug('Initialized volume: ' + volumeInfo.volumeId);
                 });
-          }.bind(this)))
-          .then(function() {
+          }))
+          .then(() => {
             console.debug('Initialized all volumes.');
             // Call the callback of the initialize function.
             callback();
@@ -155,8 +157,8 @@
             // registered by mountCompleted events.
             inCallback();
           });
-    }.bind(this));
-  }.bind(this));
+    });
+  });
 };
 
 /**
@@ -165,7 +167,7 @@
  * @private
  */
 VolumeManagerImpl.prototype.onMountCompleted_ = function(event) {
-  this.mountQueue_.run(function(callback) {
+  this.mountQueue_.run(callback => {
     switch (event.eventType) {
       case 'mount':
         var requestKey = this.makeRequestKey_(
@@ -178,10 +180,10 @@
             event.status ===
                 VolumeManagerCommon.VolumeError.UNSUPPORTED_FILESYSTEM) {
           this.addVolumeMetadata_(event.volumeMetadata).then(
-              function(volumeInfo) {
+              volumeInfo => {
                 this.finishRequest_(requestKey, event.status, volumeInfo);
                 callback();
-              }.bind(this));
+              });
         } else if (event.status ===
             VolumeManagerCommon.VolumeError.ALREADY_MOUNTED) {
           const navigationEvent =
@@ -221,7 +223,7 @@
         callback();
         break;
     }
-  }.bind(this));
+  });
 };
 
 /**
@@ -233,19 +235,19 @@
  * @return {string} Key for |this.requests_|.
  * @private
  */
-VolumeManagerImpl.prototype.makeRequestKey_ = function(requestType, argument) {
+VolumeManagerImpl.prototype.makeRequestKey_ = (requestType, argument) => {
   return requestType + ':' + argument;
 };
 
 /** @override */
 VolumeManagerImpl.prototype.mountArchive = function(
     fileUrl, successCallback, errorCallback) {
-  chrome.fileManagerPrivate.addMount(fileUrl, function(sourcePath) {
+  chrome.fileManagerPrivate.addMount(fileUrl, sourcePath => {
     console.info(
         'Mount request: url=' + fileUrl + '; sourcePath=' + sourcePath);
     const requestKey = this.makeRequestKey_('mount', sourcePath);
     this.startRequest_(requestKey, successCallback, errorCallback);
-  }.bind(this));
+  });
 };
 
 /** @override */
@@ -258,9 +260,9 @@
 };
 
 /** @override */
-VolumeManagerImpl.prototype.configure = function(volumeInfo) {
-  return new Promise(function(fulfill, reject) {
-    chrome.fileManagerPrivate.configureVolume(volumeInfo.volumeId, function() {
+VolumeManagerImpl.prototype.configure = volumeInfo => {
+  return new Promise((fulfill, reject) => {
+    chrome.fileManagerPrivate.configureVolume(volumeInfo.volumeId, () => {
       if (chrome.runtime.lastError) {
         reject(chrome.runtime.lastError.message);
       } else {
@@ -437,7 +439,7 @@
 };
 
 /** @override */
-VolumeManagerImpl.prototype.getDefaultDisplayRoot = function(callback) {
+VolumeManagerImpl.prototype.getDefaultDisplayRoot = callback => {
   console.error('Unexpectedly called VolumeManagerImpl.getDefaultDisplayRoot.');
   callback(null);
 };
@@ -506,7 +508,7 @@
  */
 VolumeManagerImpl.prototype.invokeRequestCallbacks_ = function(
     request, status, opt_volumeInfo) {
-  const callEach = function(callbacks, self, args) {
+  const callEach = (callbacks, self, args) => {
     for (let i = 0; i < callbacks.length; i++) {
       callbacks[i].apply(self, args);
     }
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 ed68f5c2..698367c 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
@@ -45,7 +45,7 @@
         },
         dispatchEvent: function(event) {
           mockChrome.fileManagerPrivate.onDriveConnectionStatusChangedListeners_
-              .forEach(function(listener) {
+              .forEach(listener => {
                 listener(event);
               });
         }
@@ -57,7 +57,7 @@
         },
         dispatchEvent: function(event) {
           mockChrome.fileManagerPrivate.onMountCompletedListeners_.forEach(
-              function(listener) {
+              listener => {
                 listener(event);
               });
         }
@@ -143,7 +143,7 @@
 
 function testGetVolumeInfo(callback) {
   reportPromise(
-      volumeManagerFactory.getInstance().then(function(volumeManager) {
+      volumeManagerFactory.getInstance().then(volumeManager => {
         const entry = new MockFileEntry(new MockFileSystem('download:Downloads'),
             '/foo/bar/bla.zip');
 
@@ -157,7 +157,7 @@
 
 function testGetDriveConnectionState(callback) {
   reportPromise(
-      volumeManagerFactory.getInstance().then(function(volumeManager) {
+      volumeManagerFactory.getInstance().then(volumeManager => {
         // Default connection state is online
         assertEquals(VolumeManagerCommon.DriveConnectionType.ONLINE,
             volumeManager.getDriveConnectionState());
@@ -185,10 +185,10 @@
       new MockFileSystem('archive:foobar.zip');
 
   reportPromise(
-      volumeManagerFactory.getInstance().then(function(volumeManager) {
+      volumeManagerFactory.getInstance().then(volumeManager => {
         const numberOfVolumes = volumeManager.volumeInfoList.length;
 
-        return new Promise(function(resolve, reject) {
+        return new Promise((resolve, reject) => {
           // Mount an archieve
           volumeManager.mountArchive(
               'filesystem:chrome-extension://extensionid/external/' +
@@ -210,13 +210,13 @@
               source: VolumeManagerCommon.Source.FILE
             }
           });
-        }).then(function(result) {
+        }).then(result => {
           assertEquals(numberOfVolumes + 1,
                        volumeManager.volumeInfoList.length);
 
-          return new Promise(function(resolve, reject) {
+          return new Promise((resolve, reject) => {
             // Unmount the mounted archievea
-            volumeManager.volumeInfoList.addEventListener('splice', function() {
+            volumeManager.volumeInfoList.addEventListener('splice', () => {
               assertEquals(numberOfVolumes,
                   volumeManager.volumeInfoList.length);
               resolve(true);
@@ -234,7 +234,7 @@
 
 function testGetCurrentProfileVolumeInfo(callback) {
   reportPromise(
-      volumeManagerFactory.getInstance().then(function(volumeManager) {
+      volumeManagerFactory.getInstance().then(volumeManager => {
         const volumeInfo = volumeManager.getCurrentProfileVolumeInfo(
             VolumeManagerCommon.VolumeType.DRIVE);
 
@@ -248,7 +248,7 @@
 
 function testGetLocationInfo(callback) {
   reportPromise(
-      volumeManagerFactory.getInstance().then(function(volumeManager) {
+      volumeManagerFactory.getInstance().then(volumeManager => {
         const downloadEntry = new MockFileEntry(
             new MockFileSystem('download:Downloads'),
             '/foo/bar/bla.zip');
@@ -391,7 +391,7 @@
 
 function testDriveMountedDuringInitialization(callback) {
   let sendMetadataListCallback;
-  chrome.fileManagerPrivate.getVolumeMetadataList = function(callback) {
+  chrome.fileManagerPrivate.getVolumeMetadataList = callback => {
     sendMetadataListCallback = callback;
   };
 
@@ -413,7 +413,7 @@
   // Complete initialization.
   sendMetadataListCallback([]);
 
-  reportPromise(instancePromise.then(function(volumeManager) {
+  reportPromise(instancePromise.then(volumeManager => {
     assertTrue(!!volumeManager.getCurrentProfileVolumeInfo(
         VolumeManagerCommon.VolumeType.DRIVE));
   }), callback);
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_util.js b/ui/file_manager/file_manager/background/js/volume_manager_util.js
index 1fcd6bf..d2d8b41 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_util.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_util.js
@@ -40,7 +40,7 @@
  *
  * @param {string} error Status string usually received from APIs.
  */
-volumeManagerUtil.validateError = function(error) {
+volumeManagerUtil.validateError = error => {
   for (const key in VolumeManagerCommon.VolumeError) {
     if (error === VolumeManagerCommon.VolumeError[key]) {
       return;
@@ -56,7 +56,7 @@
  * instance for the volume.
  * @return {!Promise<!VolumeInfo>} Promise settled with the VolumeInfo instance.
  */
-volumeManagerUtil.createVolumeInfo = function(volumeMetadata) {
+volumeManagerUtil.createVolumeInfo = volumeMetadata => {
   let localizedLabel;
   switch (volumeMetadata.volumeType) {
     case VolumeManagerCommon.VolumeType.DOWNLOADS:
@@ -102,13 +102,13 @@
       volumeMetadata.volumeId);
   return util
       .timeoutPromise(
-          new Promise(function(resolve, reject) {
+          new Promise((resolve, reject) => {
             chrome.fileSystem.requestFileSystem(
                 {
                   volumeId: volumeMetadata.volumeId,
                   writable: !volumeMetadata.isReadOnly
                 },
-                function(isolatedFileSystem) {
+                isolatedFileSystem => {
                   if (chrome.runtime.lastError) {
                     reject(chrome.runtime.lastError.message);
                   } else {
@@ -121,14 +121,14 @@
               volumeMetadata.volumeId)
       .then(
           /** @param {!FileSystem} isolatedFileSystem */
-          function(isolatedFileSystem) {
+          isolatedFileSystem => {
             // Since File System API works on isolated entries only, we need to
             // convert it back to external one.
             // TODO(mtomasz): Make Files app work on isolated entries.
             return util.timeoutPromise(
-                new Promise(function(resolve, reject) {
+                new Promise((resolve, reject) => {
                   chrome.fileManagerPrivate.resolveIsolatedEntries(
-                      [isolatedFileSystem.root], function(entries) {
+                      [isolatedFileSystem.root], entries => {
                         if (chrome.runtime.lastError) {
                           reject(chrome.runtime.lastError.message);
                         } else if (!entries[0]) {
@@ -144,7 +144,7 @@
           })
       .then(
           /** @param {!FileSystem} fileSystem */
-          function(fileSystem) {
+          fileSystem => {
             console.debug('File system obtained: ' + volumeMetadata.volumeId);
             if (volumeMetadata.volumeType ===
                 VolumeManagerCommon.VolumeType.DRIVE) {
@@ -154,8 +154,8 @@
               // it fails, accessing to some path later will just become
               // a fast-fetch and it re-triggers full-feed fetch.
               fileSystem.root.createReader().readEntries(
-                  function() { /* do nothing */ },
-                  function(error) {
+                  () => { /* do nothing */ },
+                  error => {
                     console.warn(
                         'Triggering full feed fetch has failed: ' + error.name);
                   });
@@ -178,7 +178,7 @@
           })
       .catch(
           /** @param {*} error */
-          function(error) {
+          error => {
             console.warn(
                 'Failed to mount a file system: ' + volumeMetadata.volumeId +
                 ' because of: ' + (error.stack || error));
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 ad65294..b22e7ef 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
@@ -555,35 +555,63 @@
       }
     };
 
-    var volumeInfo =
-        CommandUtil.getElementVolumeInfo(event.target, fileManager) ||
-        CommandUtil.getCurrentVolumeInfo(fileManager);
-    if (!volumeInfo) {
-      errorCallback();
-      return;
-    }
-
-    const label = volumeInfo.label || '';
-    fileManager.volumeManager.unmount(volumeInfo, () => {
+    const successCallback = () => {
       const msg = strf('A11Y_VOLUME_EJECT', label);
       fileManager.ui.speakA11yMessage(msg);
-    }, errorCallback.bind(null, volumeInfo.volumeType));
+    };
+
+    // Find volumes to unmount.
+    let volumes = [];
+    let label = '';
+    const element = event.target;
+    if (element instanceof EntryListItem) {
+      // The element is a group of removable partitions.
+      const entry = element.entry;
+      if (!entry) {
+        errorCallback();
+        return;
+      }
+      // Add child partitions to the list of volumes to be unmounted.
+      volumes = entry.getUIChildren().map(child => child.volumeInfo);
+      label = entry.label || '';
+    } else {
+      // The element is a removable volume with no partitions.
+      const volumeInfo =
+          CommandUtil.getElementVolumeInfo(element, fileManager) ||
+          CommandUtil.getCurrentVolumeInfo(fileManager);
+      if (!volumeInfo) {
+        errorCallback();
+        return;
+      }
+      volumes.push(volumeInfo);
+      label = element.label || '';
+    }
+
+    // Eject volumes of which there may be multiple.
+    for (let i = 0; i < volumes.length; i++) {
+      fileManager.volumeManager.unmount(
+          volumes[i], (i == volumes.length - 1) ? successCallback : () => {},
+          errorCallback.bind(null, volumes[i].volumeType));
+    }
   },
   /**
    * @param {!Event} event Command event.
    * @this {CommandHandler}
    */
   canExecute: function(event, fileManager) {
-    var volumeInfo =
+    const volumeInfo =
         CommandUtil.getElementVolumeInfo(event.target, fileManager) ||
         CommandUtil.getCurrentVolumeInfo(fileManager);
-    if (!volumeInfo) {
+    const entry = event.target.entry;
+    if (!volumeInfo && !entry) {
       event.canExecute = false;
       event.command.setHidden(true);
       return;
     }
 
-    var volumeType = volumeInfo.volumeType;
+    const volumeType = (event.target instanceof EntryListItem) ?
+        entry.rootType :
+        volumeInfo.volumeType;
     event.canExecute = (
         volumeType === VolumeManagerCommon.VolumeType.ARCHIVE ||
         volumeType === VolumeManagerCommon.VolumeType.REMOVABLE ||
@@ -603,63 +631,6 @@
 });
 
 /**
- * Unmounts external drive which contains partitions.
- * @type {Command}
- */
-CommandHandler.COMMANDS_['unmount-all-partitions'] = /** @type {Command} */ ({
-  /**
-   * @param {!Event} event Command event.
-   * @param {!CommandHandlerDeps} fileManager The file manager instance.
-   */
-  execute: function(event, fileManager) {
-    const errorCallback = () => {
-      fileManager.ui.alertDialog.showHtml(
-          '', str('UNMOUNT_FAILED'), null, null, null);
-    };
-
-    const successCallback = () => {
-      const rootLabel = entry.label || '';
-      const msg = strf('A11Y_VOLUME_EJECT', rootLabel);
-      fileManager.ui.speakA11yMessage(msg);
-    };
-
-    const entry = event.target.entry;
-    if (!entry) {
-      errorCallback();
-      return;
-    }
-
-    // Eject partitions one by one.
-    const partitions = entry.getUIChildren();
-    for (let i = 0; i < partitions.length; i++) {
-      const volumeInfo = partitions[i].volumeInfo;
-      fileManager.volumeManager.unmount(
-          volumeInfo, (i == partitions.length) ? successCallback : () => {},
-          errorCallback);
-    }
-  },
-
-  /**
-   * @param {!Event} event Command event.
-   * @this {CommandHandler}
-   */
-  canExecute: function(event, fileManager) {
-    const entry = event.target.entry;
-    if (!entry) {
-      event.canExecute = false;
-      event.command.setHidden(true);
-      return;
-    }
-
-    event.canExecute =
-        (entry.rootType === VolumeManagerCommon.VolumeType.REMOVABLE &&
-         entry.type_name === 'EntryList');
-    event.command.label = str('UNMOUNT_DEVICE_BUTTON_LABEL');
-    event.command.setHidden(!event.canExecute);
-  }
-});
-
-/**
  * Formats external drive.
  * @type {Command}
  */
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 fa1d29e..e5d550a 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
@@ -662,13 +662,11 @@
   ejectButton.setAttribute('tabindex', '0');
   ejectButton.addEventListener('click', (event) => {
     event.stopPropagation();
-    const unmountCommand = (this instanceof EntryListItem) ?
-        cr.doc.querySelector('command#unmount-all-partitions') :
-        cr.doc.querySelector('command#unmount');
+    const command = cr.doc.querySelector('command#unmount');
     // Let's make sure 'canExecute' state of the command is properly set for
     // the root before executing it.
-    unmountCommand.canExecuteChange(this);
-    unmountCommand.execute(this);
+    command.canExecuteChange(this);
+    command.execute(this);
   });
   rowElement.appendChild(ejectButton);
 
@@ -679,6 +677,15 @@
   ejectButton.appendChild(ripple);
 };
 
+/**
+ * Set up the context menu for directory items.
+ * @param {!cr.ui.Menu} menu Menu to be set.
+ * @private
+ */
+DirectoryItem.prototype.setContextMenu_ = function(menu) {
+  cr.ui.contextMenuHandler.setContextMenu(this, menu);
+};
+
 ////////////////////////////////////////////////////////////////////////////////
 // SubDirectoryItem
 
@@ -734,7 +741,7 @@
 
   // Sets up context menu of the item.
   if (tree.contextMenuForSubitems) {
-    cr.ui.contextMenuHandler.setContextMenu(item, tree.contextMenuForSubitems);
+    item.setContextMenu_(tree.contextMenuForSubitems);
   }
 
   // Populates children now if needed.
@@ -792,7 +799,8 @@
  * @constructor
  */
 function EntryListItem(rootType, modelItem, tree) {
-  const item = new DirectoryItem(modelItem.label, tree);
+  const item =
+      /** @type {EntryListItem} */ (new DirectoryItem(modelItem.label, tree));
   // Get the original label id defined by TreeItem, before overwriting
   // prototype.
   item.__proto__ = EntryListItem.prototype;
@@ -820,6 +828,12 @@
   }
   icon.classList.add('item-icon');
   icon.setAttribute('root-type-icon', rootType);
+
+  // Sets up context menu of the item.
+  if (tree.contextMenuForRootItems) {
+    item.setContextMenu_(tree.contextMenuForRootItems);
+  }
+
   return item;
 }
 
@@ -1011,15 +1025,6 @@
 };
 
 /**
- * Sets the context menu for volume items.
- * @param {!cr.ui.Menu} menu Menu to be set.
- * @private
- */
-VolumeItem.prototype.setContextMenu_ = function(menu) {
-  cr.ui.contextMenuHandler.setContextMenu(this, menu);
-};
-
-/**
  * @override
  */
 VolumeItem.prototype.updateSubDirectories = function(
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index 967ef24..7afa79d 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -138,7 +138,6 @@
 
       <command id="unmount" label="$i18n{UNMOUNT_DEVICE_BUTTON_LABEL}"
                shortcut="E|Shift|Ctrl">
-      <command id="unmount-all-partitions" label="$i18n{UNMOUNT_DEVICE_BUTTON_LABEL}">
       <command id="format" label="$i18n{FORMAT_DEVICE_BUTTON_LABEL}">
       <command id="configure" label="$i18n{CONFIGURE_VOLUME_BUTTON_LABEL}">
 
@@ -228,7 +227,6 @@
              menu-item-selector="cr-menu-item">
       <cr-menu-item command="#configure"></cr-menu-item>
       <cr-menu-item command="#unmount"></cr-menu-item>
-      <cr-menu-item command="#unmount-all-partitions"></cr-menu-item>
       <cr-menu-item command="#format"></cr-menu-item>
       <cr-menu-item command="#rename"></cr-menu-item>
       <cr-menu-item command="#remove-folder-shortcut"></cr-menu-item>
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 7542fff..0665e25 100644
--- a/ui/file_manager/integration_tests/file_manager/context_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/context_menu.js
@@ -672,3 +672,85 @@
 testcase.checkLinuxFilesContextMenu = function() {
   return checkMyFilesRootItemContextMenu('Linux files');
 };
+
+/**
+ * Checks the unmount command is visible on the roots context menu for a
+ * specified removable directory entry.
+ */
+async function checkUnmountRootsContextMenu(entryLabel) {
+  // Query the element by label, and wait for the contextmenu attribute which
+  // shows the menu has been set up.
+  const query = `#directory-tree [entry-label="${entryLabel}"][contextmenu]`;
+
+  // Open Files app on local downloads.
+  const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
+
+  // Mount removable volumes.
+  await sendTestMessage({name: 'mountFakePartitions'});
+
+  // Wait for removable volume to appear in the directory tree.
+  const removable = await remoteCall.waitForElement(appId, query);
+
+  // Right-click on the removable volume.
+  chrome.test.assertTrue(!!await remoteCall.callRemoteTestUtil(
+      'fakeMouseRightClick', appId, [query]));
+
+  // Wait for the context menu to appear.
+  await remoteCall.waitForElement(appId, '#roots-context-menu:not([hidden])');
+
+  // Check the unmount command is visible in the context menu.
+  const commandQuery =
+      '#roots-context-menu:not([hidden]) [command="#unmount"]:not([hidden])';
+  await remoteCall.waitForElement(appId, commandQuery);
+}
+
+/**
+ * Checks that the unmount command is shown in the context menu for a removable
+ * root with child partitions.
+ */
+testcase.checkRemovableRootContextMenu = async function() {
+  return checkUnmountRootsContextMenu('PARTITION_DRIVE_LABEL');
+};
+
+/**
+ * Checks that the unmount command is shown in the context menu for a USB.
+ */
+testcase.checkUsbContextMenu = async function() {
+  return checkUnmountRootsContextMenu('singleUSB');
+};
+
+/**
+ * Checks the roots context menu does not appear for a removable partition,
+ * The directory tree context menu should be visible and display the new-folder
+ * command.
+ */
+testcase.checkPartitionContextMenu = async function() {
+  // Query the element by label, and wait for the contextmenu attribute which
+  // shows the menu has been set up.
+  const partitionQuery = '#directory-tree .tree-children ' +
+      '[entry-label="partition-1"][contextmenu] ' +
+      '[volume-type-icon="removable"]';
+
+  // Open Files app on local downloads.
+  const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
+
+  // Mount removable volumes.
+  await sendTestMessage({name: 'mountFakePartitions'});
+
+  // Wait for partition-1 to appear in the directory tree.
+  const removable = await remoteCall.waitForElement(appId, partitionQuery);
+
+  // Right-click on the partition.
+  chrome.test.assertTrue(!!await remoteCall.callRemoteTestUtil(
+      'fakeMouseRightClick', appId, [partitionQuery]));
+
+  // Check the root context menu is hidden so there is no option to eject.
+  await remoteCall.waitForElement(appId, '#roots-context-menu[hidden]');
+
+  // Check the command to create a new-folder is visible from the directory
+  // tree context menu.
+  await remoteCall.waitForElement(
+      appId,
+      '#directory-tree-context-menu:not([hidden]) ' +
+          '[command="#new-folder"]:not([hidden])');
+};
\ No newline at end of file
diff --git a/ui/ozone/platform/wayland/wayland_connection.cc b/ui/ozone/platform/wayland/wayland_connection.cc
index 3e82f76..9cf54b61 100644
--- a/ui/ozone/platform/wayland/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/wayland_connection.cc
@@ -308,6 +308,10 @@
   data_device_->RequestDragData(mime_type, std::move(callback));
 }
 
+bool WaylandConnection::IsDragInProgress() {
+  return data_device_->IsDragEntered() || drag_data_source();
+}
+
 void WaylandConnection::ResetPointerFlags() {
   if (pointer_)
     pointer_->ResetFlags();
diff --git a/ui/ozone/platform/wayland/wayland_connection.h b/ui/ozone/platform/wayland/wayland_connection.h
index bb00ae47..59948b7 100644
--- a/ui/ozone/platform/wayland/wayland_connection.h
+++ b/ui/ozone/platform/wayland/wayland_connection.h
@@ -161,6 +161,9 @@
   void RequestDragData(const std::string& mime_type,
                        base::OnceCallback<void(const std::string&)> callback);
 
+  // Returns true when dragging is entered or started.
+  bool IsDragInProgress();
+
   // Resets flags and keyboard modifiers.
   //
   // This method is specially handy for cases when the WaylandPointer state is
diff --git a/ui/ozone/platform/wayland/wayland_data_device.h b/ui/ozone/platform/wayland/wayland_data_device.h
index f098670..c3953993 100644
--- a/ui/ozone/platform/wayland/wayland_data_device.h
+++ b/ui/ozone/platform/wayland/wayland_data_device.h
@@ -58,6 +58,8 @@
 
   wl_data_device* data_device() const { return data_device_.get(); }
 
+  bool IsDragEntered() { return drag_offer_ != nullptr; }
+
  private:
   void ReadClipboardDataFromFD(base::ScopedFD fd, const std::string& mime_type);
 
diff --git a/ui/ozone/platform/wayland/wayland_window.cc b/ui/ozone/platform/wayland/wayland_window.cc
index 1e6b464..d400c61 100644
--- a/ui/ozone/platform/wayland/wayland_window.cc
+++ b/ui/ozone/platform/wayland/wayland_window.cc
@@ -25,6 +25,7 @@
 #include "ui/ozone/platform/wayland/xdg_popup_wrapper_v6.h"
 #include "ui/ozone/platform/wayland/xdg_surface_wrapper_v5.h"
 #include "ui/ozone/platform/wayland/xdg_surface_wrapper_v6.h"
+#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 
 namespace ui {
@@ -167,6 +168,19 @@
   if (bounds_.IsEmpty())
     return;
 
+  // TODO(jkim): Consider how to support DropArrow window on tabstrip.
+  // When it starts dragging, as described the protocol, https://goo.gl/1Mskq3,
+  // the client must have an active implicit grab. If we try to create a popup
+  // window while dragging is executed, it gets 'popup_done' directly from
+  // Wayland compositor and it's destroyed through 'popup_done'. It causes
+  // a crash when aura::Window is destroyed.
+  // https://crbug.com/875164
+  if (connection_->IsDragInProgress()) {
+    surface_.reset();
+    LOG(ERROR) << "Wayland can't create a popup window during dragging.";
+    return;
+  }
+
   DCHECK(parent_window_ && !xdg_popup_);
 
   gfx::Rect bounds =
@@ -593,26 +607,39 @@
 void WaylandWindow::OnDragEnter(const gfx::PointF& point,
                                 std::unique_ptr<OSExchangeData> data,
                                 int operation) {
-  NOTIMPLEMENTED_LOG_ONCE();
+  WmDropHandler* drop_handler = GetWmDropHandler(*this);
+  if (!drop_handler)
+    return;
+  drop_handler->OnDragEnter(point, std::move(data), operation);
 }
 
 int WaylandWindow::OnDragMotion(const gfx::PointF& point,
                                 uint32_t time,
                                 int operation) {
-  NOTIMPLEMENTED_LOG_ONCE();
-  return 0;
+  WmDropHandler* drop_handler = GetWmDropHandler(*this);
+  if (!drop_handler)
+    return 0;
+
+  return drop_handler->OnDragMotion(point, operation);
 }
 
 void WaylandWindow::OnDragDrop(std::unique_ptr<OSExchangeData> data) {
-  NOTIMPLEMENTED_LOG_ONCE();
+  WmDropHandler* drop_handler = GetWmDropHandler(*this);
+  if (!drop_handler)
+    return;
+  drop_handler->OnDragDrop(std::move(data));
 }
 
 void WaylandWindow::OnDragLeave() {
-  NOTIMPLEMENTED_LOG_ONCE();
+  WmDropHandler* drop_handler = GetWmDropHandler(*this);
+  if (!drop_handler)
+    return;
+  drop_handler->OnDragLeave();
 }
 
 void WaylandWindow::OnDragSessionClose(uint32_t dnd_action) {
   std::move(drag_closed_callback_).Run(dnd_action);
+  connection_->ResetPointerFlags();
 }
 
 bool WaylandWindow::IsMinimized() const {
diff --git a/ui/platform_window/platform_window_handler/BUILD.gn b/ui/platform_window/platform_window_handler/BUILD.gn
index 65ea155..10e53a48 100644
--- a/ui/platform_window/platform_window_handler/BUILD.gn
+++ b/ui/platform_window/platform_window_handler/BUILD.gn
@@ -10,6 +10,8 @@
   sources = [
     "wm_drag_handler.cc",
     "wm_drag_handler.h",
+    "wm_drop_handler.cc",
+    "wm_drop_handler.h",
     "wm_move_resize_handler.cc",
     "wm_move_resize_handler.h",
     "wm_platform_export.h",
diff --git a/ui/platform_window/platform_window_handler/wm_drop_handler.cc b/ui/platform_window/platform_window_handler/wm_drop_handler.cc
new file mode 100644
index 0000000..27cf71ab
--- /dev/null
+++ b/ui/platform_window/platform_window_handler/wm_drop_handler.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
+
+#include "ui/base/class_property.h"
+#include "ui/platform_window/platform_window.h"
+
+DEFINE_UI_CLASS_PROPERTY_TYPE(ui::WmDropHandler*)
+
+namespace ui {
+
+DEFINE_UI_CLASS_PROPERTY_KEY(WmDropHandler*, kWmDropHandlerKey, nullptr);
+
+void SetWmDropHandler(PlatformWindow* platform_window,
+                      WmDropHandler* drop_handler) {
+  platform_window->SetProperty(kWmDropHandlerKey, drop_handler);
+}
+
+WmDropHandler* GetWmDropHandler(const PlatformWindow& platform_window) {
+  return platform_window.GetProperty(kWmDropHandlerKey);
+}
+
+}  // namespace ui
diff --git a/ui/platform_window/platform_window_handler/wm_drop_handler.h b/ui/platform_window/platform_window_handler/wm_drop_handler.h
new file mode 100644
index 0000000..768d9e3
--- /dev/null
+++ b/ui/platform_window/platform_window_handler/wm_drop_handler.h
@@ -0,0 +1,55 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DROP_HANDLER_H_
+#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DROP_HANDLER_H_
+
+#include <memory>
+
+#include "ui/gfx/native_widget_types.h"
+#include "ui/platform_window/platform_window_handler/wm_platform_export.h"
+
+namespace gfx {
+class PointF;
+}
+
+namespace ui {
+class OSExchangeData;
+class PlatformWindow;
+
+class WM_PLATFORM_EXPORT WmDropHandler {
+ public:
+  // Notifies that dragging is entered to the window. |point| is in the
+  // coordinate space of the PlatformWindow.
+  virtual void OnDragEnter(const gfx::PointF& point,
+                           std::unique_ptr<OSExchangeData> data,
+                           int operation) = 0;
+
+  // Notifies that dragging is moved. |widget_out| will be set with the
+  // widget located at |point|. |point| is in the coordinate space of the
+  // PlatformWindow. It returns the operation selected by client and the
+  // returned value should be from ui::DragDropTypes.
+  virtual int OnDragMotion(const gfx::PointF& point, int operation) = 0;
+
+  // Notifies that dragged data is dropped. When it doesn't deliver
+  // the dragged data on OnDragEnter, it should put it to |data|. The location
+  // of the drop is the location of the latest DragEnter/DragMotion. If
+  // OSExchangeData is provided on OnDragEnter, the |data| should be same as it.
+  virtual void OnDragDrop(std::unique_ptr<ui::OSExchangeData> data) = 0;
+
+  // Notifies that dragging is left.
+  virtual void OnDragLeave() = 0;
+
+ protected:
+  virtual ~WmDropHandler() {}
+};
+
+WM_PLATFORM_EXPORT void SetWmDropHandler(PlatformWindow* platform_window,
+                                         WmDropHandler* drop_handler);
+WM_PLATFORM_EXPORT WmDropHandler* GetWmDropHandler(
+    const PlatformWindow& platform_window);
+
+}  // namespace ui
+
+#endif  // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DROP_HANDLER_H_
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
index a5bf57f..dfe5f9a 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
@@ -4,12 +4,21 @@
 
 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h"
 
+#include <memory>
+
 #include "base/bind.h"
 #include "base/run_loop.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/client/cursor_client.h"
+#include "ui/aura/client/drag_drop_delegate.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/drop_target_event.h"
 #include "ui/base/dragdrop/os_exchange_data_provider_aura.h"
 #include "ui/platform_window/platform_window_delegate.h"
 #include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
@@ -17,6 +26,17 @@
 
 namespace views {
 
+namespace {
+
+aura::Window* GetTargetWindow(aura::Window* root_window,
+                              const gfx::Point& point) {
+  gfx::Point root_location(point);
+  root_window->GetHost()->ConvertScreenInPixelsToDIP(&root_location);
+  return root_window->GetEventHandlerForPoint(root_location);
+}
+
+}  // namespace
+
 DesktopDragDropClientOzone::DesktopDragDropClientOzone(
     aura::Window* root_window,
     views::DesktopNativeCursorManager* cursor_manager,
@@ -26,6 +46,8 @@
       drag_handler_(drag_handler) {}
 
 DesktopDragDropClientOzone::~DesktopDragDropClientOzone() {
+  ResetDragDropTarget();
+
   if (in_move_loop_)
     DragCancel();
 }
@@ -86,6 +108,70 @@
   NOTIMPLEMENTED_LOG_ONCE();
 }
 
+void DesktopDragDropClientOzone::OnDragEnter(
+    const gfx::PointF& point,
+    std::unique_ptr<ui::OSExchangeData> data,
+    int operation) {
+  last_drag_point_ = point;
+  drag_operation_ = operation;
+
+  // If it doesn't have |data|, it defers sending events to
+  // |drag_drop_delegate_|. It will try again before handling drop.
+  if (!data)
+    return;
+
+  os_exchange_data_ = std::move(data);
+  std::unique_ptr<ui::DropTargetEvent> event = CreateDropTargetEvent(point);
+  if (drag_drop_delegate_ && event)
+    drag_drop_delegate_->OnDragEntered(*event);
+}
+
+int DesktopDragDropClientOzone::OnDragMotion(const gfx::PointF& point,
+                                             int operation) {
+  last_drag_point_ = point;
+  drag_operation_ = operation;
+  int client_operation =
+      ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE;
+
+  if (os_exchange_data_) {
+    std::unique_ptr<ui::DropTargetEvent> event = CreateDropTargetEvent(point);
+    // If |os_exchange_data_| has a valid data, |drag_drop_delegate_| returns
+    // the operation which it expects.
+    if (drag_drop_delegate_ && event)
+      client_operation = drag_drop_delegate_->OnDragUpdated(*event);
+  }
+  return client_operation;
+}
+
+void DesktopDragDropClientOzone::OnDragDrop(
+    std::unique_ptr<ui::OSExchangeData> data) {
+  // If it doesn't have |os_exchange_data_|, it needs to update it with |data|.
+  if (!os_exchange_data_) {
+    DCHECK(data);
+    os_exchange_data_ = std::move(data);
+    std::unique_ptr<ui::DropTargetEvent> event =
+        CreateDropTargetEvent(last_drag_point_);
+    // Sends the deferred drag events to |drag_drop_delegate_| before handling
+    // drop.
+    if (drag_drop_delegate_ && event) {
+      drag_drop_delegate_->OnDragEntered(*event);
+      // TODO(jkim): It doesn't use the return value from 'OnDragUpdated' and
+      // doesn't have a chance to update the expected operation.
+      // https://crbug.com/875164
+      drag_drop_delegate_->OnDragUpdated(*event);
+    }
+  } else {
+    // If it has |os_exchange_data_|, it doesn't expect |data| on OnDragDrop.
+    DCHECK(!data);
+  }
+  PerformDrop();
+}
+
+void DesktopDragDropClientOzone::OnDragLeave() {
+  os_exchange_data_.reset();
+  ResetDragDropTarget();
+}
+
 void DesktopDragDropClientOzone::OnDragSessionClosed(int dnd_action) {
   drag_operation_ = dnd_action;
   QuitRunLoop();
@@ -107,4 +193,49 @@
   std::move(quit_closure_).Run();
 }
 
+std::unique_ptr<ui::DropTargetEvent>
+DesktopDragDropClientOzone::CreateDropTargetEvent(const gfx::PointF& location) {
+  const gfx::Point point(location.x(), location.y());
+  aura::Window* window = GetTargetWindow(root_window_, point);
+  if (!window)
+    return nullptr;
+
+  UpdateDragDropDelegate(window);
+  gfx::Point root_location(location.x(), location.y());
+  root_window_->GetHost()->ConvertScreenInPixelsToDIP(&root_location);
+  gfx::PointF target_location(root_location);
+  aura::Window::ConvertPointToTarget(root_window_, window, &target_location);
+
+  return std::make_unique<ui::DropTargetEvent>(
+      *os_exchange_data_, target_location, gfx::PointF(root_location),
+      drag_operation_);
+}
+
+void DesktopDragDropClientOzone::UpdateDragDropDelegate(aura::Window* window) {
+  aura::client::DragDropDelegate* delegate =
+      aura::client::GetDragDropDelegate(window);
+
+  if (drag_drop_delegate_ == delegate)
+    return;
+
+  ResetDragDropTarget();
+  if (delegate)
+    drag_drop_delegate_ = delegate;
+}
+
+void DesktopDragDropClientOzone::ResetDragDropTarget() {
+  if (!drag_drop_delegate_)
+    return;
+  drag_drop_delegate_->OnDragExited();
+  drag_drop_delegate_ = nullptr;
+}
+
+void DesktopDragDropClientOzone::PerformDrop() {
+  std::unique_ptr<ui::DropTargetEvent> event =
+      CreateDropTargetEvent(last_drag_point_);
+  if (drag_drop_delegate_ && event)
+    drag_operation_ = drag_drop_delegate_->OnPerformDrop(*event);
+  DragDropSessionCompleted();
+}
+
 }  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h
index e3632a7..e31f41b 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h
@@ -5,25 +5,40 @@
 #ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_OZONE_H_
 #define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_OZONE_H_
 
+#include <memory>
+
 #include "base/callback.h"
 #include "ui/aura/client/drag_drop_client.h"
 #include "ui/base/cursor/cursor.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/native_widget_types.h"
 #include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
+#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
 #include "ui/views/views_export.h"
 
+namespace aura {
+namespace client {
+class DragDropDelegate;
+}
+}  // namespace aura
+
+namespace ui {
+class DropTargetEvent;
+}
+
 namespace views {
 class DesktopNativeCursorManager;
 
 class VIEWS_EXPORT DesktopDragDropClientOzone
-    : public aura::client::DragDropClient {
+    : public aura::client::DragDropClient,
+      public ui::WmDropHandler {
  public:
   DesktopDragDropClientOzone(aura::Window* root_window,
                              views::DesktopNativeCursorManager* cursor_manager,
                              ui::WmDragHandler* drag_handler);
   ~DesktopDragDropClientOzone() override;
 
-  void OnDragSessionClosed(int operation);
-
   // Overridden from aura::client::DragDropClient:
   int StartDragAndDrop(const ui::OSExchangeData& data,
                        aura::Window* root_window,
@@ -36,16 +51,48 @@
   void AddObserver(aura::client::DragDropClientObserver* observer) override;
   void RemoveObserver(aura::client::DragDropClientObserver* observer) override;
 
+  // Overridden from void ui::WmDropHandler:
+  void OnDragEnter(const gfx::PointF& point,
+                   std::unique_ptr<ui::OSExchangeData> data,
+                   int operation) override;
+  int OnDragMotion(const gfx::PointF& point, int operation) override;
+  void OnDragDrop(std::unique_ptr<ui::OSExchangeData> data) override;
+  void OnDragLeave() override;
+
+  void OnDragSessionClosed(int operation);
+
  private:
   void DragDropSessionCompleted();
   void QuitRunLoop();
 
+  // Returns a DropTargetEvent to be passed to the DragDropDelegate, or null to
+  // abort the drag.
+  std::unique_ptr<ui::DropTargetEvent> CreateDropTargetEvent(
+      const gfx::PointF& point);
+
+  // Updates |drag_drop_delegate_| along with |window|.
+  void UpdateDragDropDelegate(aura::Window* window);
+
+  // Resets |drag_drop_delegate_|.
+  void ResetDragDropTarget();
+
+  void PerformDrop();
+
   aura::Window* const root_window_;
 
   DesktopNativeCursorManager* cursor_manager_;
 
   ui::WmDragHandler* const drag_handler_;
 
+  // The delegate corresponding to the window located at the mouse position.
+  aura::client::DragDropDelegate* drag_drop_delegate_ = nullptr;
+
+  // The data to be delivered through the drag and drop.
+  std::unique_ptr<ui::OSExchangeData> os_exchange_data_ = nullptr;
+
+  // The most recent native coordinates of a drag.
+  gfx::PointF last_drag_point_;
+
   // Cursor in use prior to the move loop starting. Restored when the move loop
   // quits.
   gfx::NativeCursor initial_cursor_;
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
index 3042f98..bcc003f 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
@@ -4,14 +4,17 @@
 
 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/bind.h"
-#include "base/memory/weak_ptr.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
+#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/views_delegate.h"
 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
@@ -21,34 +24,144 @@
 namespace views {
 
 namespace {
-
-class FakeWmDragHandler;
-
-// A fake handler, which initiates dragging.
-class FakeWmDragHandler : public ui::WmDragHandler {
+class FakePlatformWindow : public ui::PlatformWindow, public ui::WmDragHandler {
  public:
-  FakeWmDragHandler() = default;
-  ~FakeWmDragHandler() override = default;
+  FakePlatformWindow() { SetWmDragHandler(this, this); }
+  ~FakePlatformWindow() override = default;
+
+  // ui::PlatformWindow
+  void Show() override {}
+  void Hide() override {}
+  void Close() override {}
+  void PrepareForShutdown() override {}
+  void SetBounds(const gfx::Rect& bounds) override {}
+  gfx::Rect GetBounds() override { return gfx::Rect(); }
+  void SetTitle(const base::string16& title) override {}
+  void SetCapture() override {}
+  void ReleaseCapture() override {}
+  bool HasCapture() const override { return false; }
+  void ToggleFullscreen() override {}
+  void Maximize() override {}
+  void Minimize() override {}
+  void Restore() override {}
+  ui::PlatformWindowState GetPlatformWindowState() const override {
+    return ui::PlatformWindowState::PLATFORM_WINDOW_STATE_NORMAL;
+  }
+  void SetCursor(ui::PlatformCursor cursor) override {}
+  void MoveCursorTo(const gfx::Point& location) override {}
+  void ConfineCursorToBounds(const gfx::Rect& bounds) override {}
+  ui::PlatformImeController* GetPlatformImeController() override {
+    return nullptr;
+  }
+  void SetRestoredBoundsInPixels(const gfx::Rect& bounds) override {}
+  gfx::Rect GetRestoredBoundsInPixels() const override { return gfx::Rect(); }
 
   // ui::WmDragHandler
   void StartDrag(const OSExchangeData& data,
-                 const int operation,
+                 int operation,
                  gfx::NativeCursor cursor,
                  base::OnceCallback<void(int)> callback) override {
-    callback_ = std::move(callback);
+    drag_closed_callback_ = std::move(callback);
+    source_data_ = std::make_unique<OSExchangeData>(data.provider().Clone());
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(
-                       [](base::OnceCallback<void(int)> callback) {
-                         std::move(callback).Run(ui::DragDropTypes::DRAG_COPY);
-                       },
-                       std::move(callback_)));
+        FROM_HERE,
+        base::BindOnce(&FakePlatformWindow::ProcessDrag, base::Unretained(this),
+                       std::move(source_data_), operation));
+  }
+
+  void OnDragEnter(const gfx::PointF& point,
+                   std::unique_ptr<OSExchangeData> data,
+                   int operation) {
+    ui::WmDropHandler* drop_handler = ui::GetWmDropHandler(*this);
+    if (!drop_handler)
+      return;
+    drop_handler->OnDragEnter(point, std::move(data), operation);
+  }
+
+  int OnDragMotion(const gfx::PointF& point, int operation) {
+    ui::WmDropHandler* drop_handler = ui::GetWmDropHandler(*this);
+    if (!drop_handler)
+      return 0;
+
+    return drop_handler->OnDragMotion(point, operation);
+  }
+
+  void OnDragDrop(std::unique_ptr<OSExchangeData> data) {
+    ui::WmDropHandler* drop_handler = ui::GetWmDropHandler(*this);
+    if (!drop_handler)
+      return;
+    drop_handler->OnDragDrop(std::move(data));
+  }
+
+  void OnDragLeave() {
+    ui::WmDropHandler* drop_handler = ui::GetWmDropHandler(*this);
+    if (!drop_handler)
+      return;
+    drop_handler->OnDragLeave();
+  }
+
+  void CloseDrag(uint32_t dnd_action) {
+    std::move(drag_closed_callback_).Run(dnd_action);
+  }
+
+  void ProcessDrag(std::unique_ptr<OSExchangeData> data, int operation) {
+    OnDragEnter(gfx::PointF(), std::move(data), operation);
+    int updated_operation = OnDragMotion(gfx::PointF(), operation);
+    OnDragDrop(nullptr);
+    OnDragLeave();
+    CloseDrag(updated_operation);
   }
 
  private:
-  base::OnceCallback<void(int)> callback_;
-  base::WeakPtrFactory<FakeWmDragHandler> weak_ptr_factory_{this};
+  base::OnceCallback<void(int)> drag_closed_callback_;
+  std::unique_ptr<ui::OSExchangeData> source_data_;
 
-  DISALLOW_COPY_AND_ASSIGN(FakeWmDragHandler);
+  DISALLOW_COPY_AND_ASSIGN(FakePlatformWindow);
+};
+
+// DragDropDelegate which counts the number of each type of drag-drop event.
+class FakeDragDropDelegate : public aura::client::DragDropDelegate {
+ public:
+  FakeDragDropDelegate()
+      : num_enters_(0), num_updates_(0), num_exits_(0), num_drops_(0) {}
+  ~FakeDragDropDelegate() override = default;
+
+  int num_enters() const { return num_enters_; }
+  int num_updates() const { return num_updates_; }
+  int num_exits() const { return num_exits_; }
+  int num_drops() const { return num_drops_; }
+  ui::OSExchangeData* received_data() const { return received_data_.get(); }
+
+  void SetOperation(int operation) { destination_operation_ = operation; }
+
+ private:
+  // aura::client::DragDropDelegate:
+  void OnDragEntered(const ui::DropTargetEvent& event) override {
+    ++num_enters_;
+  }
+
+  int OnDragUpdated(const ui::DropTargetEvent& event) override {
+    ++num_updates_;
+    return destination_operation_;
+  }
+
+  void OnDragExited() override { ++num_exits_; }
+
+  int OnPerformDrop(const ui::DropTargetEvent& event) override {
+    ++num_drops_;
+    received_data_ =
+        std::make_unique<OSExchangeData>(event.data().provider().Clone());
+    return destination_operation_;
+  }
+
+  int num_enters_;
+  int num_updates_;
+  int num_exits_;
+  int num_drops_;
+  std::unique_ptr<ui::OSExchangeData> received_data_;
+  int destination_operation_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeDragDropDelegate);
 };
 
 }  // namespace
@@ -58,7 +171,7 @@
   DesktopDragDropClientOzoneTest() = default;
   ~DesktopDragDropClientOzoneTest() override = default;
 
-  int StartDragAndDrop() {
+  int StartDragAndDrop(int operation) {
     ui::OSExchangeData data;
     data.SetString(base::ASCIIToUTF16("Test"));
     SkBitmap drag_bitmap;
@@ -69,8 +182,7 @@
 
     return client_->StartDragAndDrop(
         data, widget_->GetNativeWindow()->GetRootWindow(),
-        widget_->GetNativeWindow(), gfx::Point(),
-        ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE,
+        widget_->GetNativeWindow(), gfx::Point(), operation,
         ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
   }
 
@@ -88,25 +200,35 @@
     widget_->Init(params);
     widget_->Show();
 
+    // Creates FakeDragDropDelegate and set it for |window|.
     aura::Window* window = widget_->GetNativeWindow();
+    dragdrop_delegate_ = std::make_unique<FakeDragDropDelegate>();
+    aura::client::SetDragDropDelegate(window, dragdrop_delegate_.get());
+
     cursor_manager_ = std::make_unique<DesktopNativeCursorManager>();
-    drag_handler_ = std::make_unique<FakeWmDragHandler>();
+    platform_window_ = std::make_unique<FakePlatformWindow>();
+    ui::WmDragHandler* drag_handler = ui::GetWmDragHandler(*(platform_window_));
+    // Creates DesktopDragDropClientOzone with |window| and |drag_handler|.
     client_ = std::make_unique<DesktopDragDropClientOzone>(
-        window, cursor_manager_.get(), drag_handler_.get());
+        window, cursor_manager_.get(), drag_handler);
+    SetWmDropHandler(platform_window_.get(), client_.get());
   }
 
   void TearDown() override {
     client_.reset();
     cursor_manager_.reset();
-    drag_handler_.reset();
+    platform_window_.reset();
     widget_.reset();
     ViewsTestBase::TearDown();
   }
 
+ protected:
+  std::unique_ptr<FakeDragDropDelegate> dragdrop_delegate_;
+  std::unique_ptr<FakePlatformWindow> platform_window_;
+
  private:
   std::unique_ptr<DesktopDragDropClientOzone> client_;
   std::unique_ptr<DesktopNativeCursorManager> cursor_manager_;
-  std::unique_ptr<FakeWmDragHandler> drag_handler_;
 
   // The widget used to initiate drags.
   std::unique_ptr<Widget> widget_;
@@ -115,8 +237,55 @@
 };
 
 TEST_F(DesktopDragDropClientOzoneTest, StartDrag) {
-  int result = StartDragAndDrop();
-  EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
+  // Set the operation which the destination can accept.
+  dragdrop_delegate_->SetOperation(ui::DragDropTypes::DRAG_COPY);
+  // Start Drag and Drop with the operations suggested.
+  int operation = StartDragAndDrop(ui::DragDropTypes::DRAG_COPY |
+                                   ui::DragDropTypes::DRAG_MOVE);
+  // The |operation| decided through negotiation should be 'DRAG_COPY'.
+  EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, operation);
+
+  EXPECT_EQ(1, dragdrop_delegate_->num_enters());
+  EXPECT_EQ(1, dragdrop_delegate_->num_updates());
+  EXPECT_EQ(1, dragdrop_delegate_->num_drops());
+  EXPECT_EQ(1, dragdrop_delegate_->num_exits());
+}
+
+TEST_F(DesktopDragDropClientOzoneTest, ReceiveDrag) {
+  // Set the operation which the destination can accept.
+  int operation = ui::DragDropTypes::DRAG_MOVE;
+  dragdrop_delegate_->SetOperation(operation);
+
+  // Set the data which will be delivered.
+  const base::string16 sample_data = base::ASCIIToUTF16("ReceiveDrag");
+  std::unique_ptr<ui::OSExchangeData> data =
+      std::make_unique<ui::OSExchangeData>();
+  data->SetString(sample_data);
+
+  // Simulate that the drag enter/motion/drop/leave events happen with the
+  // |suggested_operation|.
+  int suggested_operation =
+      ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE;
+  platform_window_->OnDragEnter(gfx::PointF(), std::move(data),
+                                suggested_operation);
+  int updated_operation =
+      platform_window_->OnDragMotion(gfx::PointF(), suggested_operation);
+  platform_window_->OnDragDrop(nullptr);
+  platform_window_->OnDragLeave();
+
+  // The |updated_operation| decided through negotiation should be
+  // 'ui::DragDropTypes::DRAG_MOVE'.
+  EXPECT_EQ(operation, updated_operation);
+
+  base::string16 string_data;
+  dragdrop_delegate_->received_data()->GetString(&string_data);
+  EXPECT_EQ(sample_data, string_data);
+
+  EXPECT_EQ(1, dragdrop_delegate_->num_enters());
+  EXPECT_EQ(1, dragdrop_delegate_->num_enters());
+  EXPECT_EQ(1, dragdrop_delegate_->num_updates());
+  EXPECT_EQ(1, dragdrop_delegate_->num_drops());
+  EXPECT_EQ(1, dragdrop_delegate_->num_exits());
 }
 
 }  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index b00de5a..1aead10 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -119,8 +119,13 @@
 DesktopWindowTreeHostPlatform::CreateDragDropClient(
     DesktopNativeCursorManager* cursor_manager) {
   ui::WmDragHandler* drag_handler = ui::GetWmDragHandler(*(platform_window()));
-  return std::make_unique<DesktopDragDropClientOzone>(window(), cursor_manager,
-                                                      drag_handler);
+  std::unique_ptr<DesktopDragDropClientOzone> drag_drop_client =
+      std::make_unique<DesktopDragDropClientOzone>(window(), cursor_manager,
+                                                   drag_handler);
+  // Set a class property key, which allows |drag_drop_client| to be used for
+  // drop action.
+  SetWmDropHandler(platform_window(), drag_drop_client.get());
+  return std::move(drag_drop_client);
 }
 
 void DesktopWindowTreeHostPlatform::Close() {
@@ -142,6 +147,7 @@
 
 void DesktopWindowTreeHostPlatform::CloseNow() {
   auto weak_ref = weak_factory_.GetWeakPtr();
+  SetWmDropHandler(platform_window(), nullptr);
   // Deleting the PlatformWindow may not result in OnClosed() being called, if
   // not behave as though it was.
   SetPlatformWindow(nullptr);