diff --git a/AUTHORS b/AUTHORS
index 32fc92e..9f4e6ff 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -601,6 +601,7 @@
 Max Vujovic <mvujovic@adobe.com>
 Mayank Gupta <mayank.g1@samsung.com>
 Mayur Kankanwadi <mayurk.vk@samsung.com>
+Md Abdullah Al Alamin <a.alamin.cse@gmail.com>
 Md Jobed Hossain <jrony15@gmail.com>
 Md Sami Uddin <md.sami@samsung.com>
 Michael Cirone <mikecirone@gmail.com>
diff --git a/DEPS b/DEPS
index a516c3a..c06f3ac 100644
--- a/DEPS
+++ b/DEPS
@@ -138,7 +138,7 @@
   # 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': '09f5aedf2cc886296641658fdb800ced1e7f0b7e',
+  'skia_revision': 'eca66b32fdb91344b2a22d98c405794c3fded66a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -150,11 +150,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'c104b2d24a6f0593d5bf5ccce4b594e644a5abcb',
+  'angle_revision': '98f2167125a8d204ed120d483d79a7af577806ca',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '95b1db9619fb0f5f232c09995bc00729273f74ee',
+  'swiftshader_revision': 'bb575d48d5f9f96cef08a5d23b0d51ce3c57ae1a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -201,7 +201,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '5b31e690a48d5f7a8ba38825a603a57bca282038',
+  'catapult_revision': '58f6c4682e28f328945fa14a616d6b4e1cdde6ad',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -807,7 +807,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a3fa5283c9f7561d1e53a3dc009ea76b45ab68bb',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '6e80eaeb04000f78322820e286e91aa474e8d0f8',
       'condition': 'checkout_linux',
   },
 
@@ -901,7 +901,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '6d88284607d2d3deeb9fc88627d536092eb21f11',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'a549bb81752365526f6d7334f00961ea08689211',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1182,7 +1182,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '9e68bfc4b622e1d509f5767cd715cf059215eb43',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'fc15cad3689508822ab2d0bb0200509ff35dbc8c',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1391,7 +1391,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@64bd50cc7d890d38cafa0c0b81da34bcdc79b40d',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d167c5b89a4246460710b3b818b0498d1dfb2941',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 45699aaf..dab8eea 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1585,9 +1585,6 @@
     'startup': {
       'filepath': 'chrome/browser/ui/startup/',
     },
-    'streams': {
-      'filepath': 'content/browser/streams/',
-    },
     'styleguide': {
       'filepath': '^styleguide/',
     },
@@ -1776,11 +1773,10 @@
                   'media/gpu/',
     },
     'virtual_keyboard': {
-      'filepath': 'ui/keyboard/'\
+      'filepath': 'ash/keyboard/'\
                   '|chrome/test/data/chromeos/virtual_keyboard/'\
                   '|extensions/browser/api/virtual_keyboard_private/'\
-                  '|ash/system/virtual_keyboard/'\
-                  '|ash/virtual_keyboard',
+                  '|ash/system/virtual_keyboard/',
     },
     'virtual_reality': {
       # Includes VR, AR, and XR.
@@ -2566,7 +2562,6 @@
                    'timvolodine@chromium.org'],
     'startup': ['grt+watch@chromium.org',
                 'pastarmovj+watch@chromium.org'],
-    'streams': ['zork+watch@chromium.org'],
     'styleguide': ['danakj+watch@chromium.org',
                    'jbroman+cpp@chromium.org',
                    'vmpstr+watch@chromium.org'],
@@ -2638,8 +2633,7 @@
     'video': ['posciak+watch@chromium.org'],
     'video_capture': ['chfremer+watch@chromium.org'],
     'video_gpu': ['acourbot+watch@chromium.org'],
-    'virtual_keyboard': ['blakeo+virtualkb@chromium.org',
-                         'dfaden+virtualkb@google.com',
+    'virtual_keyboard': ['dfaden+virtualkb@google.com',
                          'yhanada+watchvk@chromium.org',
                          'shend+watch@chromium.org'],
     'virtual_reality': ['feature-vr-reviews@chromium.org'],
diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS
index 808603e..628d5bb 100644
--- a/android_webview/browser/DEPS
+++ b/android_webview/browser/DEPS
@@ -81,6 +81,7 @@
   "+third_party/blink/public/common/mediastream",
   # QuotaStatusCode required by AwQuotaManagerBridge.
   "+third_party/blink/public/mojom/quota",
+  "+third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h",
   # Interface required for in-process input event handling.
   "+third_party/blink/public/web/WebCompositorInputHandler.h",
   # For find-in-page
diff --git a/android_webview/browser/aw_web_contents_delegate.cc b/android_webview/browser/aw_web_contents_delegate.cc
index 36fbb09b..54a8e1b 100644
--- a/android_webview/browser/aw_web_contents_delegate.cc
+++ b/android_webview/browser/aw_web_contents_delegate.cc
@@ -32,6 +32,7 @@
 #include "jni/AwWebContentsDelegate_jni.h"
 #include "net/base/filename_util.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 using base::android::AttachCurrentThread;
 using base::android::ConvertUTF16ToJavaString;
@@ -274,9 +275,10 @@
     content::MediaResponseCallback callback) {
   AwContents* aw_contents = AwContents::FromWebContents(web_contents);
   if (!aw_contents) {
-    std::move(callback).Run(blink::MediaStreamDevices(),
-                            blink::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN,
-                            std::unique_ptr<content::MediaStreamUI>());
+    std::move(callback).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN,
+        std::unique_ptr<content::MediaStreamUI>());
     return;
   }
   aw_contents->GetPermissionRequestHandler()->SendRequest(
diff --git a/android_webview/browser/permission/media_access_permission_request.cc b/android_webview/browser/permission/media_access_permission_request.cc
index 2c6bda7..633798b 100644
--- a/android_webview/browser/permission/media_access_permission_request.cc
+++ b/android_webview/browser/permission/media_access_permission_request.cc
@@ -8,6 +8,7 @@
 
 #include "android_webview/browser/permission/aw_permission_request.h"
 #include "content/public/browser/media_capture_devices.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 using blink::MediaStreamDevice;
 using blink::MediaStreamDevices;
@@ -48,8 +49,9 @@
   std::unique_ptr<content::MediaStreamUI> ui;
   MediaStreamDevices devices;
   if (!allowed) {
-    std::move(callback_).Run(devices, blink::MEDIA_DEVICE_PERMISSION_DENIED,
-                             std::move(ui));
+    std::move(callback_).Run(
+        devices, blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+        std::move(ui));
     return;
   }
 
@@ -74,10 +76,11 @@
     if (device)
       devices.push_back(*device);
   }
-  std::move(callback_).Run(devices,
-                           devices.empty() ? blink::MEDIA_DEVICE_NO_HARDWARE
-                                           : blink::MEDIA_DEVICE_OK,
-                           std::move(ui));
+  std::move(callback_).Run(
+      devices,
+      devices.empty() ? blink::mojom::MediaStreamRequestResult::NO_HARDWARE
+                      : blink::mojom::MediaStreamRequestResult::OK,
+      std::move(ui));
 }
 
 const GURL& MediaAccessPermissionRequest::GetOrigin() {
diff --git a/android_webview/browser/permission/media_access_permission_request_unittest.cc b/android_webview/browser/permission/media_access_permission_request_unittest.cc
index 845b13d..7b0a7d0 100644
--- a/android_webview/browser/permission/media_access_permission_request_unittest.cc
+++ b/android_webview/browser/permission/media_access_permission_request_unittest.cc
@@ -5,6 +5,7 @@
 #include "android_webview/browser/permission/media_access_permission_request.h"
 #include "base/bind.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 namespace android_webview {
 
@@ -65,11 +66,11 @@
   std::string first_audio_device_id_;
   std::string first_video_device_id_;
   blink::MediaStreamDevices devices_;
-  blink::MediaStreamRequestResult result_;
+  blink::mojom::MediaStreamRequestResult result_;
 
  private:
   void Callback(const blink::MediaStreamDevices& devices,
-                blink::MediaStreamRequestResult result,
+                blink::mojom::MediaStreamRequestResult result,
                 std::unique_ptr<content::MediaStreamUI> ui) {
     devices_ = devices;
     result_ = result;
@@ -82,7 +83,7 @@
   request->NotifyRequestResult(true);
 
   EXPECT_EQ(2u, devices_.size());
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result_);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result_);
 
   bool audio_exist = false;
   bool video_exist = false;
@@ -106,7 +107,7 @@
   request->NotifyRequestResult(true);
 
   EXPECT_EQ(2u, devices_.size());
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result_);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result_);
 
   bool audio_exist = false;
   bool video_exist = false;
@@ -129,7 +130,7 @@
       CreateRequest(std::string(), std::string());
   request->NotifyRequestResult(false);
   EXPECT_TRUE(devices_.empty());
-  EXPECT_EQ(blink::MEDIA_DEVICE_PERMISSION_DENIED, result_);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, result_);
 }
 
 }  // namespace android_webview
diff --git a/base/task/README.md b/base/task/README.md
index a3710c54..e437e480 100644
--- a/base/task/README.md
+++ b/base/task/README.md
@@ -11,3 +11,4 @@
 
 * [Threading and tasks](/docs/threading_and_tasks.md)
 * [Callbacks](/docs/callback.md)
+* [Vision for future API changes](https://docs.google.com/document/d/1pySz2xeJ6kLlbzDnS2jqAC1F8T_6pLEV8pgaMfURXAw/edit)
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index f0b9ba16..5c509c02 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -137,7 +137,6 @@
 class BrowserTestBase;
 class CategorizedWorkerPool;
 class DesktopCaptureDevice;
-class DWriteFontLookupTableBuilder;
 class GpuProcessTransportFactory;
 class InProcessUtilityThread;
 class NestedMessagePumpAndroid;
@@ -396,7 +395,6 @@
   friend class chrome_cleaner::SystemReportComponent;
   friend class content::BrowserMainLoop;
   friend class content::BrowserProcessSubThread;
-  friend class content::DWriteFontLookupTableBuilder;
   friend class content::ServiceWorkerContextClient;
   friend class content::SessionStorageDatabase;
   friend class functions::ExecScriptScopedAllowBaseSyncPrimitives;
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index a3f21b7..0f42bc7 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1431,6 +1431,12 @@
         "-Wno-narrowing",
       ]
 
+      # -Wno-class-memaccess warns about hash table and vector in blink.
+      # But the violation is intentional.
+      if (!is_nacl) {
+        cflags_cc += [ "-Wno-class-memaccess" ]
+      }
+
       # -Wunused-local-typedefs is broken in gcc,
       # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63872
       cflags += [ "-Wno-unused-local-typedefs" ]
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 392f0ae..ab2e4008 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8912027476835416640
\ No newline at end of file
+8911706893625634704
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 2cf19d4..b527f1a 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8912026800475747664
\ No newline at end of file
+8911702506200835040
\ No newline at end of file
diff --git a/chrome/VERSION b/chrome/VERSION
index e878b53f..35ea3eb1 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=77
 MINOR=0
-BUILD=3813
+BUILD=3814
 PATCH=0
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_bottom_sheet_content.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_bottom_sheet_content.xml
index 3635bd1..40e8182 100644
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_bottom_sheet_content.xml
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_bottom_sheet_content.xml
@@ -36,6 +36,7 @@
             android:id="@+id/profile_image"
             android:layout_width="24dp"
             android:layout_height="24dp"
+            android:contentDescription="@string/preferences"
             app:srcCompat="@drawable/logo_avatar_anonymous"/>
     </LinearLayout>
 
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index b8f6bc1..59820c0 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-77.0.3812.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-77.0.3813.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/app_management_strings.grdp b/chrome/app/app_management_strings.grdp
index 90aac942..3c20cf7d 100644
--- a/chrome/app/app_management_strings.grdp
+++ b/chrome/app/app_management_strings.grdp
@@ -76,4 +76,7 @@
   <message name="IDS_APP_MANAGEMENT_VERSION" desc="Label for version of an app in the app settings page.">
     Version: <ph name="VERSION">$1<ex>4.0</ex></ph>
   </message>
+  <message name="IDS_APP_MANAGEMENT_PIN_ENFORCED_BY_POLICY" desc="Short text for the pin/unpin context menu to tell user the setting is enforced by policy of an app.">
+    Pinned by administrator
+  </message>
 </grit-part>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 79f188d..35f52891 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1775,6 +1775,10 @@
      flag_descriptions::kBypassAppBannerEngagementChecksName,
      flag_descriptions::kBypassAppBannerEngagementChecksDescription, kOsAll,
      SINGLE_VALUE_TYPE(switches::kBypassAppBannerEngagementChecks)},
+    {"enable-desktop-pwas-unified-install",
+     flag_descriptions::kDesktopPWAsUnifiedInstallName,
+     flag_descriptions::kDesktopPWAsUnifiedInstallDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(features::kDesktopPWAsUnifiedInstall)},
     {"enable-desktop-pwas-link-capturing",
      flag_descriptions::kEnableDesktopPWAsLinkCapturingName,
      flag_descriptions::kEnableDesktopPWAsLinkCapturingDescription, kOsDesktop,
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index 71c46822..4fc0ce60 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -138,13 +138,15 @@
 }
 
 void UpdateAppPermissions(
-    const base::flat_map<arc::mojom::AppPermission, bool>& new_permissions,
+    const base::flat_map<arc::mojom::AppPermission,
+                         arc::mojom::PermissionStatePtr>& new_permissions,
     std::vector<apps::mojom::PermissionPtr>* permissions) {
   for (const auto& new_permission : new_permissions) {
     auto permission = apps::mojom::Permission::New();
     permission->permission_id = static_cast<uint32_t>(new_permission.first);
     permission->value_type = apps::mojom::PermissionValueType::kBool;
-    permission->value = static_cast<uint32_t>(new_permission.second);
+    permission->value = static_cast<uint32_t>(new_permission.second->granted);
+    permission->is_managed = new_permission.second->managed;
 
     permissions->push_back(std::move(permission));
   }
diff --git a/chrome/browser/apps/app_service/extension_apps.cc b/chrome/browser/apps/app_service/extension_apps.cc
index 4e4b037..c1115cbb 100644
--- a/chrome/browser/apps/app_service/extension_apps.cc
+++ b/chrome/browser/apps/app_service/extension_apps.cc
@@ -28,6 +28,7 @@
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/services/app_service/public/mojom/types.mojom.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "extensions/browser/extension_prefs.h"
@@ -467,10 +468,16 @@
         setting_val = apps::mojom::TriState::kAsk;
     }
 
+    content_settings::SettingInfo setting_info;
+    host_content_settings_map->GetWebsiteSetting(url, url, type, std::string(),
+                                                 &setting_info);
+
     auto permission = apps::mojom::Permission::New();
     permission->permission_id = static_cast<uint32_t>(type);
     permission->value_type = apps::mojom::PermissionValueType::kTriState;
     permission->value = static_cast<uint32_t>(setting_val);
+    permission->is_managed =
+        setting_info.source == content_settings::SETTING_SOURCE_POLICY;
 
     target->push_back(std::move(permission));
   }
diff --git a/chrome/browser/chromeos/arc/accessibility/OWNERS b/chrome/browser/chromeos/arc/accessibility/OWNERS
index 81077236..7f0944a 100644
--- a/chrome/browser/chromeos/arc/accessibility/OWNERS
+++ b/chrome/browser/chromeos/arc/accessibility/OWNERS
@@ -1,5 +1,5 @@
 dtseng@chromium.org
-yawano@chromium.org
+sarakato@chromium.org
 
 file://ui/accessibility/OWNERS
 
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.cc b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
index 2ac1dec..6c5ad4a 100644
--- a/chrome/browser/chromeos/crostini/crostini_registry_service.cc
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
@@ -379,6 +379,18 @@
   return false;
 }
 
+std::string CrostiniRegistryService::Registration::PackageId() const {
+  if (is_terminal_app_)
+    return std::string();
+  if (pref_.is_none())
+    return std::string();
+  const base::Value* package_id =
+      pref_.FindKeyOfType(kAppPackageIdKey, base::Value::Type::STRING);
+  if (!package_id)
+    return std::string();
+  return package_id->GetString();
+}
+
 bool CrostiniRegistryService::Registration::CanUninstall() const {
   if (pref_.is_none())
     return false;
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.h b/chrome/browser/chromeos/crostini/crostini_registry_service.h
index ba24934..f933a50 100644
--- a/chrome/browser/chromeos/crostini/crostini_registry_service.h
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service.h
@@ -87,6 +87,8 @@
     std::set<std::string> Keywords() const;
     bool NoDisplay() const;
 
+    std::string PackageId() const;
+
     base::Time InstallTime() const;
     base::Time LastLaunchTime() const;
 
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc b/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc
index dab07e7..90c34c6 100644
--- a/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc
@@ -79,6 +79,8 @@
   std::set<std::string> mime_types = {"text/plain", "text/x-python"};
   bool no_display = true;
   std::string executable_file_name = "execName";
+  std::string package_id =
+      "vim;2:8.0.0197-4+deb9u1;amd64;installed:debian-stable";
 
   std::string app_id = CrostiniTestHelper::GenerateAppId(
       desktop_file_id, vm_name, container_name);
@@ -92,6 +94,7 @@
   app->set_desktop_file_id(desktop_file_id);
   app->set_no_display(no_display);
   app->set_executable_file_name(executable_file_name);
+  app->set_package_id(package_id);
 
   for (const auto& localized_name : name) {
     App::LocaleString::Entry* entry = app->mutable_name()->add_values();
@@ -129,6 +132,7 @@
   EXPECT_EQ(result->MimeTypes(), mime_types);
   EXPECT_EQ(result->NoDisplay(), no_display);
   EXPECT_EQ(result->ExecutableFileName(), executable_file_name);
+  EXPECT_EQ(result->PackageId(), package_id);
 }
 
 TEST_F(CrostiniRegistryServiceTest, Observer) {
@@ -508,4 +512,27 @@
   EXPECT_EQ(result_no_exec->ExecutableFileName(), "");
 }
 
+TEST_F(CrostiniRegistryServiceTest, SetAndGetPackageId) {
+  std::string package_id =
+      "vim;2:8.0.0197-4+deb9u1;amd64;installed:debian-stable";
+  std::string app_id_valid_package_id =
+      CrostiniTestHelper::GenerateAppId("app", "vm", "container");
+  std::string app_id_no_package_id =
+      CrostiniTestHelper::GenerateAppId("noPackageId", "vm", "container");
+  ApplicationList app_list =
+      CrostiniTestHelper::BasicAppList("app", "vm", "container");
+  *app_list.add_apps() = CrostiniTestHelper::BasicApp("noPackageId");
+
+  app_list.mutable_apps(0)->set_package_id(package_id);
+  service()->UpdateApplicationList(app_list);
+
+  base::Optional<CrostiniRegistryService::Registration>
+      result_valid_package_id =
+          service()->GetRegistration(app_id_valid_package_id);
+  base::Optional<CrostiniRegistryService::Registration> result_no_package_id =
+      service()->GetRegistration(app_id_no_package_id);
+  EXPECT_EQ(result_valid_package_id->PackageId(), package_id);
+  EXPECT_EQ(result_no_package_id->PackageId(), "");
+}
+
 }  // namespace crostini
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 39181f9..c66b7d7 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -617,6 +617,7 @@
         TestCase("driveClickFirstSearchResult").EnableDriveFs(),
         TestCase("drivePressEnterToSearch").DisableDriveFs(),
         TestCase("drivePressEnterToSearch").EnableDriveFs(),
+        TestCase("drivePressClearSearch").EnableDriveFs(),
         TestCase("drivePressCtrlAFromSearch").DisableDriveFs(),
         TestCase("drivePressCtrlAFromSearch").EnableDriveFs(),
         TestCase("driveBackupPhotos").DisableDriveFs(),
@@ -894,7 +895,8 @@
     ::testing::Values(TestCase("showGridViewDownloads"),
                       TestCase("showGridViewDownloads").InGuestMode(),
                       TestCase("showGridViewDrive").DisableDriveFs(),
-                      TestCase("showGridViewDrive").EnableDriveFs()));
+                      TestCase("showGridViewDrive").EnableDriveFs(),
+                      TestCase("showGridViewButtonSwitches")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     Providers, /* providers.js */
@@ -1018,6 +1020,13 @@
     FilesAppBrowserTest,
     ::testing::Values(TestCase("navigationScrollsWhenClipped")));
 
+WRAPPED_INSTANTIATE_TEST_SUITE_P(
+    Search, /* search.js */
+    FilesAppBrowserTest,
+    ::testing::Values(TestCase("searchDownloadsWithResults"),
+                      TestCase("searchDownloadsWithNoResults"),
+                      TestCase("searchDownloadsClearSearch")));
+
 // Structure to describe an account info.
 struct TestAccountInfo {
   const char* const gaia_id;
diff --git a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
index 0f2d2d3..768f2e8 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
@@ -724,6 +724,10 @@
              IDS_FILE_BROWSER_SEARCH_NO_MATCHING_FILES_HTML);
   SET_STRING("SEARCH_TEXT_LABEL", IDS_FILE_BROWSER_SEARCH_TEXT_LABEL);
   SET_STRING("SEARCH_CLEAR_LABEL", IDS_FILE_BROWSER_SEARCH_CLEAR_LABEL);
+  SET_STRING("SEARCH_A11Y_NO_RESULT", IDS_FILE_BROWSER_SEARCH_A11Y_NO_RESULT);
+  SET_STRING("SEARCH_A11Y_RESULT", IDS_FILE_BROWSER_SEARCH_A11Y_RESULT);
+  SET_STRING("SEARCH_A11Y_CLEAR_SEARCH",
+             IDS_FILE_BROWSER_SEARCH_A11Y_CLEAR_SEARCH);
   SET_STRING("SELECT_ALL_COMMAND_LABEL",
              IDS_FILE_BROWSER_SELECT_ALL_COMMAND_LABEL);
   SET_STRING("TASKS_BUTTON_LABEL", IDS_FILE_BROWSER_TASKS_BUTTON_LABEL);
@@ -744,6 +748,10 @@
              IDS_FILE_BROWSER_CHANGE_TO_LISTVIEW_BUTTON_LABEL);
   SET_STRING("CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL",
              IDS_FILE_BROWSER_CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL);
+  SET_STRING("FILE_LIST_CHANGED_TO_LIST_VIEW",
+             IDS_FILE_BROWSER_FILE_LIST_CHANGED_TO_LIST_VIEW);
+  SET_STRING("FILE_LIST_CHANGED_TO_LIST_THUMBNAIL_VIEW",
+             IDS_FILE_BROWSER_FILE_LIST_CHANGED_TO_THUMBNAIL_VIEW);
   SET_STRING("CANCEL_SELECTION_BUTTON_LABEL",
              IDS_FILE_BROWSER_CANCEL_SELECTION_BUTTON_LABEL);
   SET_STRING("SET_WALLPAPER_BUTTON_LABEL",
diff --git a/chrome/browser/chromeos/login/saml/saml_password_expiry_notification.cc b/chrome/browser/chromeos/login/saml/saml_password_expiry_notification.cc
index 48d6d7d..cad3e28 100644
--- a/chrome/browser/chromeos/login/saml/saml_password_expiry_notification.cc
+++ b/chrome/browser/chromeos/login/saml/saml_password_expiry_notification.cc
@@ -33,11 +33,13 @@
 
 using message_center::HandleNotificationClickDelegate;
 using message_center::Notification;
+using message_center::NotificationObserver;
 using message_center::NotificationType;
 using message_center::NotifierId;
 using message_center::NotifierType;
 using message_center::RichNotificationData;
 using message_center::SystemNotificationWarningLevel;
+using message_center::ThunkNotificationDelegate;
 
 namespace chromeos {
 
@@ -70,9 +72,8 @@
 // ie "Chromium OS" or similar.
 const base::NoDestructor<base::string16> kDisplaySource;
 
-// These extra attributes aren't needed, so they are left empty.
-const base::NoDestructor<GURL> kOriginUrl;
-const base::NoDestructor<RichNotificationData> kRichNotificationData;
+// No origin URL is needed since the notification comes from the system.
+const base::NoDestructor<GURL> kEmptyOriginUrl;
 
 // Line separator in the notification body.
 const base::NoDestructor<base::string16> kLineSeparator(
@@ -93,6 +94,30 @@
   return base::JoinString(body_lines, *kLineSeparator);
 }
 
+void ShowNotificationImpl(
+    Profile* profile,
+    int less_than_n_days,
+    scoped_refptr<message_center::NotificationDelegate> delegate) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  const base::string16 title = GetTitleText(less_than_n_days);
+  const base::string16 body = GetBodyText(less_than_n_days);
+
+  // TODO(olsen): Add button to notification - see UI mock.
+  RichNotificationData rich_notification_data;
+  std::unique_ptr<Notification> notification = ash::CreateSystemNotification(
+      kNotificationType, kNotificationId, title, body, *kDisplaySource,
+      *kEmptyOriginUrl, *kNotifierId, rich_notification_data, delegate, kIcon,
+      kWarningLevel);
+
+  NotificationDisplayService* nds =
+      NotificationDisplayServiceFactory::GetForProfile(profile);
+  // Calling close before display ensures that the notification pops up again
+  // even if it is already shown.
+  nds->Close(kNotificationHandlerType, kNotificationId);
+  nds->Display(kNotificationHandlerType, *notification, /*metadata=*/nullptr);
+}
+
 // A time delta of length one hour.
 const base::TimeDelta kOneHour = base::TimeDelta::FromHours(1);
 // A time delta of length one day.
@@ -119,8 +144,12 @@
 // The Rechecker checks periodically if the notification should be shown or
 // updated. When it checks depends on when the password will expire.
 // There is only at most one Rechecker at a time - for the primary user.
-class Rechecker : public ash::SessionActivationObserver {
+class Rechecker : public ash::SessionActivationObserver,
+                  public NotificationObserver {
  public:
+  // Shows the notification for the primary profile.
+  void ShowNotification(int less_than_n_days);
+
   // Checks again if the notification should be shown, and maybe shows it.
   void Recheck();
 
@@ -134,6 +163,11 @@
   void OnSessionActivated(bool activated) override {}  // Not needed.
   void OnLockStateChanged(bool locked) override;
 
+  // NotificationObserver:
+  void Click(const base::Optional<int>& button_index,
+             const base::Optional<base::string16>& reply) override;
+  void Close(bool by_user) override;
+
   // Ensures the singleton is initialized and started for the given profile.
   static void StartForProfile(Profile* profile);
   // Stops and deletes the Rechecker singleton.
@@ -149,11 +183,29 @@
 
   Profile* profile_;
   const AccountId account_id_;
-  base::WeakPtrFactory<Rechecker> weak_ptr_factory_{this};
+  bool reshow_on_unlock_ = false;
+
+  // Weak ptr factory for handling interaction with notifications - these
+  // pointers shouldn't be invalidated until this class is deleted.
+  base::WeakPtrFactory<NotificationObserver> observer_weak_ptr_factory_{this};
+  // Weak ptr factory for posting tasks. Invalidating these pointers will
+  // cancel upcoming tasks.
+  base::WeakPtrFactory<Rechecker> task_weak_ptr_factory_{this};
+
+  // Give test helper access to internals.
+  friend SamlPasswordExpiryNotificationTestHelper;
 
   DISALLOW_COPY_AND_ASSIGN(Rechecker);
 };
 
+void Rechecker::ShowNotification(int less_than_n_days) {
+  ShowNotificationImpl(profile_, less_than_n_days,
+                       base::MakeRefCounted<ThunkNotificationDelegate>(
+                           observer_weak_ptr_factory_.GetWeakPtr()));
+  // When a notification is currently showing, reshow it on every unlock.
+  reshow_on_unlock_ = true;
+}
+
 void Rechecker::Recheck() {
   // This cancels any pending call to Recheck - we don't want some bug to cause
   // us to queue up lots of calls to Recheck in the future, we only want one.
@@ -186,7 +238,7 @@
   if (less_than_n_days <= advance_warning_days) {
     // The password is expired, or expires in less than |advance_warning_days|.
     // So we show a notification immediately.
-    ShowSamlPasswordExpiryNotification(profile_, less_than_n_days);
+    ShowNotification(less_than_n_days);
     // We check again whether to reshow / update the notification after one day:
     RecheckAfter(kOneDay);
 
@@ -205,22 +257,37 @@
 void Rechecker::RecheckAfter(base::TimeDelta delay) {
   base::PostDelayedTaskWithTraits(
       FROM_HERE, kRecheckTaskTraits,
-      base::BindOnce(&Rechecker::Recheck, weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&Rechecker::Recheck, task_weak_ptr_factory_.GetWeakPtr()),
       std::max(delay, kOneHour));
   // This always waits at least one hour before calling Recheck again - we don't
   // want some bug to cause this code to run every millisecond.
 }
 
 void Rechecker::CancelPendingRecheck() {
-  weak_ptr_factory_.InvalidateWeakPtrs();
+  task_weak_ptr_factory_.InvalidateWeakPtrs();
 }
 
 void Rechecker::OnLockStateChanged(bool locked) {
-  if (!locked) {
-    // TODO(olsen): Reshow the notification when screen is unlocked.
+  // If a notification is currently showing, we show a new version of it every
+  // time the user unlocks the screen. This makes the notification pop up once
+  // more - just after typing the password is a good time to remind the user.
+  if (!locked && reshow_on_unlock_) {
+    Recheck();
   }
 }
 
+void Rechecker::Click(const base::Optional<int>& button_index,
+                      const base::Optional<base::string16>& reply) {
+  // TODO(olsen): Add a button, only handle clicks on the button itself.
+  PasswordChangeDialog::Show(profile_);
+}
+
+void Rechecker::Close(bool by_user) {
+  // When a notification is dismissed, we then don't pop it up again each time
+  // the user unlocks the screen.
+  reshow_on_unlock_ = false;
+}
+
 // static
 void Rechecker::StartForProfile(Profile* profile) {
   if (!instance_) {
@@ -280,20 +347,10 @@
 void ShowSamlPasswordExpiryNotification(Profile* profile,
                                         int less_than_n_days) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  const base::string16 title = GetTitleText(less_than_n_days);
-  const base::string16 body = GetBodyText(less_than_n_days);
-
-  auto click_delegate = base::MakeRefCounted<HandleNotificationClickDelegate>(
-      base::BindRepeating(&PasswordChangeDialog::Show, profile));
-
-  std::unique_ptr<Notification> notification = ash::CreateSystemNotification(
-      kNotificationType, kNotificationId, title, body, *kDisplaySource,
-      *kOriginUrl, *kNotifierId, *kRichNotificationData, click_delegate, kIcon,
-      kWarningLevel);
-
-  NotificationDisplayServiceFactory::GetForProfile(profile)->Display(
-      kNotificationHandlerType, *notification, /*metadata=*/nullptr);
+  ShowNotificationImpl(
+      profile, less_than_n_days,
+      base::MakeRefCounted<HandleNotificationClickDelegate>(
+          base::BindRepeating(&PasswordChangeDialog::Show, profile)));
 }
 
 void DismissSamlPasswordExpiryNotification(Profile* profile) {
@@ -302,9 +359,14 @@
       kNotificationHandlerType, kNotificationId);
 }
 
-void ResetSamlPasswordExpiryNotificationForTesting() {
+void SamlPasswordExpiryNotificationTestHelper::ResetForTesting() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  Rechecker::Stop();
+  delete Rechecker::instance_;
+}
+
+void SamlPasswordExpiryNotificationTestHelper::SimulateUnlockForTesting() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  Rechecker::instance_->OnLockStateChanged(/*locked=*/false);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/saml/saml_password_expiry_notification.h b/chrome/browser/chromeos/login/saml/saml_password_expiry_notification.h
index 23f3a32..7266aa70 100644
--- a/chrome/browser/chromeos/login/saml/saml_password_expiry_notification.h
+++ b/chrome/browser/chromeos/login/saml/saml_password_expiry_notification.h
@@ -26,8 +26,15 @@
 // Hides the password expiry notification if it is currently shown.
 void DismissSamlPasswordExpiryNotification(Profile* profile);
 
-// Stop waiting for the password to expire and free up any resources.
-void ResetSamlPasswordExpiryNotificationForTesting();
+// Exposes extra functionality that should only be used during testing.
+class SamlPasswordExpiryNotificationTestHelper {
+ public:
+  // Simulate unlocking the screen, which makes the notification pop up again.
+  void SimulateUnlockForTesting();
+
+  // Stop waiting for the password to expire and free up any resources.
+  void ResetForTesting();
+};
 
 }  // namespace chromeos
 
diff --git a/chrome/browser/chromeos/login/saml/saml_password_expiry_notification_unittest.cc b/chrome/browser/chromeos/login/saml/saml_password_expiry_notification_unittest.cc
index b542dfe..e6194499 100644
--- a/chrome/browser/chromeos/login/saml/saml_password_expiry_notification_unittest.cc
+++ b/chrome/browser/chromeos/login/saml/saml_password_expiry_notification_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/chromeos/login/saml/saml_password_expiry_notification.h"
 
+#include "ash/public/cpp/session/session_activation_observer.h"
+#include "ash/public/cpp/session/session_controller.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_task_environment.h"
 #include "chrome/browser/browser_process.h"
@@ -31,6 +33,7 @@
 
 constexpr base::TimeDelta kStartTime = base::TimeDelta::FromMilliseconds(1);
 
+constexpr base::TimeDelta kOneHour = base::TimeDelta::FromHours(1);
 constexpr base::TimeDelta kOneDay = base::TimeDelta::FromDays(1);
 constexpr base::TimeDelta kAdvanceWarningTime = base::TimeDelta::FromDays(14);
 constexpr base::TimeDelta kOneYear = base::TimeDelta::FromDays(365);
@@ -68,7 +71,7 @@
 
   void TearDown() override {
     display_service_tester_.reset();
-    ResetSamlPasswordExpiryNotificationForTesting();
+    expiry_notification_test_helper_.ResetForTesting();
   }
 
  protected:
@@ -94,6 +97,8 @@
   TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal()};
   TestingProfile* profile_;
 
+  SamlPasswordExpiryNotificationTestHelper expiry_notification_test_helper_;
+
   std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
   std::unique_ptr<NotificationDisplayServiceTester> display_service_tester_;
 };
@@ -291,4 +296,47 @@
   ExpectNotificationAndDismiss();
 }
 
+TEST_F(SamlPasswordExpiryNotificationTest, ReshowOnUnlock) {
+  SetExpirationTime(base::Time::Now() + kAdvanceWarningTime / 2);
+  MaybeShowSamlPasswordExpiryNotification(profile_);
+
+  // Notification is shown immediately.
+  EXPECT_TRUE(Notification().has_value());
+  base::Time first_shown_at = Notification()->timestamp();
+
+  // This notification is still present an hour later - but it is the same
+  // notification as before. So it is no longer shown prominently on screen.
+  test_environment_.FastForwardBy(kOneHour);
+  EXPECT_TRUE(Notification().has_value());
+  EXPECT_EQ(first_shown_at, Notification()->timestamp());
+
+  // But when the screen is unlocked, the old notification is replaced with a
+  // newer one. The new one is prominently shown on screen for a few seconds.
+  expiry_notification_test_helper_.SimulateUnlockForTesting();
+  EXPECT_TRUE(Notification().has_value());
+  EXPECT_NE(first_shown_at, Notification()->timestamp());
+}
+
+TEST_F(SamlPasswordExpiryNotificationTest, DontReshowWhenDismissed) {
+  SetExpirationTime(base::Time::Now() + kAdvanceWarningTime / 2);
+  MaybeShowSamlPasswordExpiryNotification(profile_);
+
+  // Notification is shown immediately.
+  EXPECT_TRUE(Notification().has_value());
+
+  // If dismissed, the notification won't reappear within the next hour, since
+  // we don't want to nag the user continuously.
+  DismissSamlPasswordExpiryNotification(profile_);
+  test_environment_.FastForwardBy(kOneHour);
+  EXPECT_FALSE(Notification().has_value());
+
+  // Nor will it reappear if the user unlocks the screen.
+  expiry_notification_test_helper_.SimulateUnlockForTesting();
+  EXPECT_FALSE(Notification().has_value());
+
+  // But it will eventually reappear the next day.
+  test_environment_.FastForwardBy(kOneDay);
+  EXPECT_TRUE(Notification().has_value());
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/content_settings/host_content_settings_map_factory.cc b/chrome/browser/content_settings/host_content_settings_map_factory.cc
index bcc5cfe3..a3797e6 100644
--- a/chrome/browser/content_settings/host_content_settings_map_factory.cc
+++ b/chrome/browser/content_settings/host_content_settings_map_factory.cc
@@ -75,12 +75,8 @@
 
   // In incognito mode, retrieve the host content settings map of the parent
   // profile in order to ensure the preferences have been migrated.
-  // TODO(crbug.com/277296): Remove check that profile does not equal the
-  // original profile once TestingProfile::ForceIncognito is gone.
-  if (profile->IsIncognitoProfile() &&
-      profile != profile->GetOriginalProfile()) {
+  if (profile->IsIncognitoProfile())
     GetForProfile(profile->GetOriginalProfile());
-  }
 
   scoped_refptr<HostContentSettingsMap> settings_map(new HostContentSettingsMap(
       profile->GetPrefs(),
diff --git a/chrome/browser/extensions/api/streams_private/streams_private_api.cc b/chrome/browser/extensions/api/streams_private/streams_private_api.cc
index 9e81f0a..3841b7ad 100644
--- a/chrome/browser/extensions/api/streams_private/streams_private_api.cc
+++ b/chrome/browser/extensions/api/streams_private/streams_private_api.cc
@@ -10,7 +10,6 @@
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/stream_info.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.h"
@@ -26,7 +25,6 @@
     int frame_tree_node_id,
     int render_process_id,
     int render_frame_id,
-    std::unique_ptr<content::StreamInfo> stream,
     content::mojom::TransferrableURLLoaderPtr transferrable_loader,
     const GURL& original_url) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -69,9 +67,9 @@
   GURL handler_url(Extension::GetBaseURLFromExtensionId(extension_id).spec() +
                    handler->handler_url());
   int tab_id = ExtensionTabUtil::GetTabId(web_contents);
-  std::unique_ptr<StreamContainer> stream_container(new StreamContainer(
-      std::move(stream), tab_id, embedded, handler_url, extension_id,
-      std::move(transferrable_loader), original_url));
+  std::unique_ptr<StreamContainer> stream_container(
+      new StreamContainer(tab_id, embedded, handler_url, extension_id,
+                          std::move(transferrable_loader), original_url));
   MimeHandlerStreamManager::Get(browser_context)
       ->AddStream(view_id, std::move(stream_container), frame_tree_node_id,
                   render_process_id, render_frame_id);
diff --git a/chrome/browser/extensions/api/streams_private/streams_private_api.h b/chrome/browser/extensions/api/streams_private/streams_private_api.h
index 32908bc..5506ae5e9 100644
--- a/chrome/browser/extensions/api/streams_private/streams_private_api.h
+++ b/chrome/browser/extensions/api/streams_private/streams_private_api.h
@@ -11,18 +11,13 @@
 #include "base/macros.h"
 #include "content/public/common/transferrable_url_loader.mojom.h"
 
-namespace content {
-struct StreamInfo;
-}
-
 namespace extensions {
 
 // TODO(devlin): This is now only used for the MimeTypesHandler API. We should
 // rename and move it to make that clear. https://crbug.com/890401.
 class StreamsPrivateAPI {
  public:
-  // Send the onExecuteMimeTypeHandler event to |extension_id|.
-  // The data for the document will be readable from |stream|. If the viewer is
+  // Send the onExecuteMimeTypeHandler event to |extension_id|. If the viewer is
   // being opened in a BrowserPlugin, specify a non-empty |view_id| of the
   // plugin. |embedded| should be set to whether the document is embedded
   // within another document. The |frame_tree_node_id| parameter is used for
@@ -41,7 +36,6 @@
       int frame_tree_node_id,
       int render_process_id,
       int render_frame_id,
-      std::unique_ptr<content::StreamInfo> stream,
       content::mojom::TransferrableURLLoaderPtr transferrable_loader,
       const GURL& original_url);
 };
diff --git a/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.cc b/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.cc
index 97bf387..a965ebb 100644
--- a/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.cc
+++ b/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.cc
@@ -15,6 +15,7 @@
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/process_manager.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 using content::WebContents;
 
@@ -94,10 +95,11 @@
   DVLOG(2) << "Allowing " << devices.size()
            << " capture devices for OffscreenTab content.";
 
-  std::move(callback).Run(devices,
-                          devices.empty() ? blink::MEDIA_DEVICE_INVALID_STATE
-                                          : blink::MEDIA_DEVICE_OK,
-                          nullptr);
+  std::move(callback).Run(
+      devices,
+      devices.empty() ? blink::mojom::MediaStreamRequestResult::INVALID_STATE
+                      : blink::mojom::MediaStreamRequestResult::OK,
+      nullptr);
 }
 
 void OffscreenTabsOwner::DestroyTab(OffscreenTab* tab) {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index c22402d..5e8395e 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1005,6 +1005,11 @@
     "expiry_milestone": 79
   },
   {
+    "name": "enable-desktop-pwas-unified-install",
+    "owners": [ "desktop-pwas-team@google.com" ],
+    "expiry_milestone": 78
+  },
+  {
     "name": "enable-devtools-experiments",
     "owners": [ "//third_party/blink/renderer/devtools/OWNERS" ],
     // This is a catch-all for ongoing devtools experiments.
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index b1331223..99f90cc 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -550,6 +550,11 @@
     "When on a site that passes PWA installation requirements show a button in "
     "the omnibox for installing it.";
 
+const char kDesktopPWAsUnifiedInstallName[] =
+    "Desktop PWAs Unified Installation.";
+const char kDesktopPWAsUnifiedInstallDescription[] =
+    "New unified installation process for Desktop PWAs.";
+
 const char kEnableSystemWebAppsName[] = "System Web Apps";
 const char kEnableSystemWebAppsDescription[] =
     "Experimental system for using the Desktop PWA framework for running System"
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 396474b..fb9da327 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -348,6 +348,9 @@
 extern const char kDesktopPWAsOmniboxInstallName[];
 extern const char kDesktopPWAsOmniboxInstallDescription[];
 
+extern const char kDesktopPWAsUnifiedInstallName[];
+extern const char kDesktopPWAsUnifiedInstallDescription[];
+
 extern const char kEnableSystemWebAppsName[];
 extern const char kEnableSystemWebAppsDescription[];
 
diff --git a/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc b/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc
index 4b270f0..9b577b5 100644
--- a/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc
+++ b/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc
@@ -25,8 +25,6 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host_view.h"
-#include "content/public/browser/stream_handle.h"
-#include "content/public/browser/stream_info.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
@@ -150,34 +148,19 @@
       const GURL& url,
       std::string* view_id) {
     *view_id = base::GenerateGUID();
-    auto stream_info = std::make_unique<content::StreamInfo>();
-    stream_info->handle = std::make_unique<FakeStreamHandle>(url);
-    stream_info->original_url = url;
-    stream_info->mime_type = "application/pdf";
-    stream_info->response_headers =
+    auto transferrable_loader = content::mojom::TransferrableURLLoader::New();
+    transferrable_loader->url = url;
+    transferrable_loader->head.mime_type = "application/pdf";
+    transferrable_loader->head.headers =
         base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK");
     return std::make_unique<extensions::StreamContainer>(
-        std::move(stream_info), 0 /* tab_id */, false /* embedded */,
+        0 /* tab_id */, false /* embedded */,
         GURL(std::string(extensions::kExtensionScheme) +
              kTestExtensionId) /* handler_url */,
-        kTestExtensionId, nullptr /* transferrable_loader */, url);
+        kTestExtensionId, std::move(transferrable_loader), url);
   }
 
  private:
-  class FakeStreamHandle : public content::StreamHandle {
-   public:
-    explicit FakeStreamHandle(const GURL& url) : url_(url) {}
-    ~FakeStreamHandle() override {}
-
-    const GURL& GetURL() override { return url_; }
-    void AddCloseListener(const base::RepeatingClosure& callback) override {}
-
-   private:
-    const GURL url_;
-
-    DISALLOW_COPY_AND_ASSIGN(FakeStreamHandle);
-  };
-
   TestGuestViewManagerFactory factory_;
   content::WebContents* guest_web_contents_;
   content::WebContents* embedder_web_contents_;
diff --git a/chrome/browser/media/media_access_handler.cc b/chrome/browser/media/media_access_handler.cc
index 3b7bc9a..3fd6509 100644
--- a/chrome/browser/media/media_access_handler.cc
+++ b/chrome/browser/media/media_access_handler.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/web_contents.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 bool MediaAccessHandler::IsInsecureCapturingInProgress(int render_process_id,
                                                        int render_frame_id) {
@@ -40,9 +41,10 @@
   // TODO(grunell): The invalid state result should be changed to a new denied
   // result + a dcheck to ensure at least one of audio or video types is
   // capture.
-  blink::MediaStreamRequestResult result =
-      (audio_allowed || video_allowed) ? blink::MEDIA_DEVICE_NO_HARDWARE
-                                       : blink::MEDIA_DEVICE_INVALID_STATE;
+  blink::mojom::MediaStreamRequestResult result =
+      (audio_allowed || video_allowed)
+          ? blink::mojom::MediaStreamRequestResult::NO_HARDWARE
+          : blink::mojom::MediaStreamRequestResult::INVALID_STATE;
 
   // Get the exact audio or video device if an id is specified.
   // We only set any error result here and before running the callback change
@@ -78,7 +80,7 @@
 
   std::unique_ptr<content::MediaStreamUI> ui;
   if (!devices.empty()) {
-    result = blink::MEDIA_DEVICE_OK;
+    result = blink::mojom::MediaStreamRequestResult::OK;
     ui = MediaCaptureDevicesDispatcher::GetInstance()
              ->GetMediaStreamCaptureIndicator()
              ->RegisterMediaStream(web_contents, devices);
diff --git a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
index 49ce12a..ac912e3 100644
--- a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
@@ -45,6 +45,7 @@
 #include "extensions/common/switches.h"
 #include "net/base/url_util.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -174,14 +175,15 @@
   // it after checking permission.
   // TODO(grunell): It would be good to change this result for something else,
   // probably a new one.
-  blink::MediaStreamRequestResult result = blink::MEDIA_DEVICE_INVALID_STATE;
+  blink::mojom::MediaStreamRequestResult result =
+      blink::mojom::MediaStreamRequestResult::INVALID_STATE;
 
 #if defined(OS_CHROMEOS)
   if (features::IsMultiProcessMash()) {
     // TODO(crbug.com/806366): Screen capture support for mash.
     NOTIMPLEMENTED() << "Screen capture not yet implemented in --mash";
     screen_capture_enabled = false;
-    result = blink::MEDIA_DEVICE_NOT_SUPPORTED;
+    result = blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED;
   }
 #endif  // defined(OS_CHROMEOS)
 
@@ -255,8 +257,9 @@
 
     // The only case when devices can be empty is if the user has denied
     // permission.
-    result = devices.empty() ? blink::MEDIA_DEVICE_PERMISSION_DENIED
-                             : blink::MEDIA_DEVICE_OK;
+    result = devices.empty()
+                 ? blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED
+                 : blink::mojom::MediaStreamRequestResult::OK;
   }
 
   std::move(callback).Run(devices, result, std::move(ui));
@@ -295,8 +298,9 @@
   std::unique_ptr<content::MediaStreamUI> ui;
 
   if (request.video_type != blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE) {
-    std::move(callback).Run(devices, blink::MEDIA_DEVICE_INVALID_STATE,
-                            std::move(ui));
+    std::move(callback).Run(
+        devices, blink::mojom::MediaStreamRequestResult::INVALID_STATE,
+        std::move(ui));
     return;
   }
 
@@ -336,8 +340,9 @@
 
   // Received invalid device id.
   if (media_id.type == content::DesktopMediaID::TYPE_NONE) {
-    std::move(callback).Run(devices, blink::MEDIA_DEVICE_INVALID_STATE,
-                            std::move(ui));
+    std::move(callback).Run(
+        devices, blink::mojom::MediaStreamRequestResult::INVALID_STATE,
+        std::move(ui));
     return;
   }
 
@@ -381,7 +386,8 @@
       GetApplicationTitle(web_contents, extension),
       GetApplicationTitle(web_contents, extension));
   UpdateExtensionTrusted(request, extension);
-  std::move(callback).Run(devices, blink::MEDIA_DEVICE_OK, std::move(ui));
+  std::move(callback).Run(devices, blink::mojom::MediaStreamRequestResult::OK,
+                          std::move(ui));
 }
 
 void DesktopCaptureAccessHandler::ProcessChangeSourceRequest(
@@ -393,8 +399,9 @@
 
   std::unique_ptr<DesktopMediaPicker> picker = picker_factory_->CreatePicker();
   if (!picker) {
-    std::move(callback).Run(blink::MediaStreamDevices(),
-                            blink::MEDIA_DEVICE_INVALID_STATE, nullptr);
+    std::move(callback).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::INVALID_STATE, nullptr);
     return;
   }
 
@@ -482,14 +489,14 @@
 
   PendingAccessRequest& pending_request = *queue.front();
   blink::MediaStreamDevices devices;
-  blink::MediaStreamRequestResult request_result =
-      blink::MEDIA_DEVICE_PERMISSION_DENIED;
+  blink::mojom::MediaStreamRequestResult request_result =
+      blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
   const extensions::Extension* extension = pending_request.extension;
   std::unique_ptr<content::MediaStreamUI> ui;
   if (media_id.is_null()) {
-    request_result = blink::MEDIA_DEVICE_PERMISSION_DENIED;
+    request_result = blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
   } else {
-    request_result = blink::MEDIA_DEVICE_OK;
+    request_result = blink::mojom::MediaStreamRequestResult::OK;
     // Determine if the extension is required to display a notification.
     const bool display_notification =
         display_notification_ && ShouldDisplayNotification(extension);
diff --git a/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc b/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc
index 7dc427f..12f59419f 100644
--- a/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc
@@ -20,6 +20,7 @@
 #include "content/public/browser/web_contents.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 class DesktopCaptureAccessHandlerTest : public ChromeRenderViewHostTestHarness {
  public:
@@ -36,7 +37,7 @@
 
   void ProcessRequest(
       const content::DesktopMediaID& fake_desktop_media_id_response,
-      blink::MediaStreamRequestResult* request_result,
+      blink::mojom::MediaStreamRequestResult* request_result,
       blink::MediaStreamDevices* devices_result,
       blink::MediaStreamRequestType request_type,
       bool request_audio) {
@@ -56,10 +57,10 @@
     base::RunLoop wait_loop;
     content::MediaResponseCallback callback = base::BindOnce(
         [](base::RunLoop* wait_loop,
-           blink::MediaStreamRequestResult* request_result,
+           blink::mojom::MediaStreamRequestResult* request_result,
            blink::MediaStreamDevices* devices_result,
            const blink::MediaStreamDevices& devices,
-           blink::MediaStreamRequestResult result,
+           blink::mojom::MediaStreamRequestResult result,
            std::unique_ptr<content::MediaStreamUI> ui) {
           *request_result = result;
           *devices_result = devices;
@@ -93,38 +94,38 @@
 
 TEST_F(DesktopCaptureAccessHandlerTest,
        ChangeSourceWithoutAudioRequestPermissionGiven) {
-  blink::MediaStreamRequestResult result;
+  blink::mojom::MediaStreamRequestResult result;
   blink::MediaStreamDevices devices;
   ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
                                          content::DesktopMediaID::kFakeId),
                  &result, &devices, blink::MEDIA_DEVICE_UPDATE,
                  false /*request_audio*/);
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
   EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, devices[0].type);
 }
 
 TEST_F(DesktopCaptureAccessHandlerTest,
        ChangeSourceWithAudioRequestPermissionGiven) {
-  blink::MediaStreamRequestResult result;
+  blink::mojom::MediaStreamRequestResult result;
   blink::MediaStreamDevices devices;
   ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
                                          content::DesktopMediaID::kFakeId,
                                          true /* audio_share */),
                  &result, &devices, blink::MEDIA_DEVICE_UPDATE,
                  true /* request_audio */);
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(2u, devices.size());
   EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, devices[0].type);
   EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, devices[1].type);
 }
 
 TEST_F(DesktopCaptureAccessHandlerTest, ChangeSourcePermissionDenied) {
-  blink::MediaStreamRequestResult result;
+  blink::mojom::MediaStreamRequestResult result;
   blink::MediaStreamDevices devices;
   ProcessRequest(content::DesktopMediaID(), &result, &devices,
                  blink::MEDIA_DEVICE_UPDATE, false /*request audio*/);
-  EXPECT_EQ(blink::MEDIA_DEVICE_PERMISSION_DENIED, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, result);
   EXPECT_EQ(0u, devices.size());
 }
 
@@ -203,7 +204,7 @@
   const size_t kTestFlagCount = 2;
   picker_factory_->SetTestFlags(test_flags, kTestFlagCount);
 
-  blink::MediaStreamRequestResult result;
+  blink::mojom::MediaStreamRequestResult result;
   blink::MediaStreamDevices devices;
   base::RunLoop wait_loop[kTestFlagCount];
   for (size_t i = 0; i < kTestFlagCount; ++i) {
@@ -213,10 +214,10 @@
         blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, false);
     content::MediaResponseCallback callback = base::BindOnce(
         [](base::RunLoop* wait_loop,
-           blink::MediaStreamRequestResult* request_result,
+           blink::mojom::MediaStreamRequestResult* request_result,
            blink::MediaStreamDevices* devices_result,
            const blink::MediaStreamDevices& devices,
-           blink::MediaStreamRequestResult result,
+           blink::mojom::MediaStreamRequestResult result,
            std::unique_ptr<content::MediaStreamUI> ui) {
           *request_result = result;
           *devices_result = devices;
@@ -229,7 +230,7 @@
   wait_loop[0].Run();
   EXPECT_TRUE(test_flags[0].picker_created);
   EXPECT_TRUE(test_flags[0].picker_deleted);
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
   EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, devices[0].type);
 
@@ -238,7 +239,7 @@
   EXPECT_FALSE(test_flags[1].picker_deleted);
   wait_loop[1].Run();
   EXPECT_TRUE(test_flags[1].picker_deleted);
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
   EXPECT_EQ(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, devices[0].type);
   EXPECT_FALSE(devices[0].IsSameDevice(first_device));
diff --git a/chrome/browser/media/webrtc/display_media_access_handler.cc b/chrome/browser/media/webrtc/display_media_access_handler.cc
index 1f6b4fe8..16176bc 100644
--- a/chrome/browser/media/webrtc/display_media_access_handler.cc
+++ b/chrome/browser/media/webrtc/display_media_access_handler.cc
@@ -23,6 +23,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 // Holds pending request information so that we display one picker UI at a time
 // for each content::WebContents.
@@ -90,16 +91,18 @@
   // TODO(emircan): Remove this once Mac UI doesn't use a window.
   if (web_contents->GetVisibility() != content::Visibility::VISIBLE) {
     LOG(ERROR) << "Do not allow getDisplayMedia() on a backgrounded page.";
-    std::move(callback).Run(blink::MediaStreamDevices(),
-                            blink::MEDIA_DEVICE_INVALID_STATE, nullptr);
+    std::move(callback).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::INVALID_STATE, nullptr);
     return;
   }
 #endif  // defined(OS_MACOSX)
 
   std::unique_ptr<DesktopMediaPicker> picker = picker_factory_->CreatePicker();
   if (!picker) {
-    std::move(callback).Run(blink::MediaStreamDevices(),
-                            blink::MEDIA_DEVICE_INVALID_STATE, nullptr);
+    std::move(callback).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::INVALID_STATE, nullptr);
     return;
   }
 
@@ -187,13 +190,13 @@
 
   PendingAccessRequest& pending_request = *queue.front();
   blink::MediaStreamDevices devices;
-  blink::MediaStreamRequestResult request_result =
-      blink::MEDIA_DEVICE_PERMISSION_DENIED;
+  blink::mojom::MediaStreamRequestResult request_result =
+      blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
   std::unique_ptr<content::MediaStreamUI> ui;
   if (media_id.is_null()) {
-    request_result = blink::MEDIA_DEVICE_PERMISSION_DENIED;
+    request_result = blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
   } else {
-    request_result = blink::MEDIA_DEVICE_OK;
+    request_result = blink::mojom::MediaStreamRequestResult::OK;
     const auto& visible_url = url_formatter::FormatUrlForSecurityDisplay(
         web_contents->GetLastCommittedURL(),
         url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
diff --git a/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc b/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
index 9d3cb2ed..62042a7a 100644
--- a/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
+++ b/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
@@ -20,6 +20,7 @@
 #include "content/public/browser/web_contents.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 class DisplayMediaAccessHandlerTest : public ChromeRenderViewHostTestHarness {
  public:
@@ -36,7 +37,7 @@
 
   void ProcessRequest(
       const content::DesktopMediaID& fake_desktop_media_id_response,
-      blink::MediaStreamRequestResult* request_result,
+      blink::mojom::MediaStreamRequestResult* request_result,
       blink::MediaStreamDevices* devices_result,
       bool request_audio) {
     FakeDesktopMediaPickerFactory::TestFlags test_flags[] = {
@@ -54,10 +55,10 @@
     base::RunLoop wait_loop;
     content::MediaResponseCallback callback = base::BindOnce(
         [](base::RunLoop* wait_loop,
-           blink::MediaStreamRequestResult* request_result,
+           blink::mojom::MediaStreamRequestResult* request_result,
            blink::MediaStreamDevices* devices_result,
            const blink::MediaStreamDevices& devices,
-           blink::MediaStreamRequestResult result,
+           blink::mojom::MediaStreamRequestResult result,
            std::unique_ptr<content::MediaStreamUI> ui) {
           *request_result = result;
           *devices_result = devices;
@@ -90,25 +91,25 @@
 };
 
 TEST_F(DisplayMediaAccessHandlerTest, PermissionGiven) {
-  blink::MediaStreamRequestResult result;
+  blink::mojom::MediaStreamRequestResult result;
   blink::MediaStreamDevices devices;
   ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
                                          content::DesktopMediaID::kFakeId),
                  &result, &devices, false /* request_audio */);
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
   EXPECT_EQ(blink::MEDIA_DISPLAY_VIDEO_CAPTURE, devices[0].type);
   EXPECT_TRUE(devices[0].display_media_info.has_value());
 }
 
 TEST_F(DisplayMediaAccessHandlerTest, PermissionGivenToRequestWithAudio) {
-  blink::MediaStreamRequestResult result;
+  blink::mojom::MediaStreamRequestResult result;
   blink::MediaStreamDevices devices;
   content::DesktopMediaID fake_media_id(content::DesktopMediaID::TYPE_SCREEN,
                                         content::DesktopMediaID::kFakeId,
                                         true /* audio_share */);
   ProcessRequest(fake_media_id, &result, &devices, true /* request_audio */);
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(2u, devices.size());
   EXPECT_EQ(blink::MEDIA_DISPLAY_VIDEO_CAPTURE, devices[0].type);
   EXPECT_TRUE(devices[0].display_media_info.has_value());
@@ -117,11 +118,11 @@
 }
 
 TEST_F(DisplayMediaAccessHandlerTest, PermissionDenied) {
-  blink::MediaStreamRequestResult result;
+  blink::mojom::MediaStreamRequestResult result;
   blink::MediaStreamDevices devices;
   ProcessRequest(content::DesktopMediaID(), &result, &devices,
                  true /* request_audio */);
-  EXPECT_EQ(blink::MEDIA_DEVICE_PERMISSION_DENIED, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, result);
   EXPECT_EQ(0u, devices.size());
 }
 
@@ -202,7 +203,7 @@
   const size_t kTestFlagCount = 2;
   picker_factory_->SetTestFlags(test_flags, kTestFlagCount);
 
-  blink::MediaStreamRequestResult result;
+  blink::mojom::MediaStreamRequestResult result;
   blink::MediaStreamDevices devices;
   base::RunLoop wait_loop[kTestFlagCount];
   for (size_t i = 0; i < kTestFlagCount; ++i) {
@@ -212,10 +213,10 @@
         blink::MEDIA_DISPLAY_VIDEO_CAPTURE, false);
     content::MediaResponseCallback callback = base::BindOnce(
         [](base::RunLoop* wait_loop,
-           blink::MediaStreamRequestResult* request_result,
+           blink::mojom::MediaStreamRequestResult* request_result,
            blink::MediaStreamDevices* devices_result,
            const blink::MediaStreamDevices& devices,
-           blink::MediaStreamRequestResult result,
+           blink::mojom::MediaStreamRequestResult result,
            std::unique_ptr<content::MediaStreamUI> ui) {
           *request_result = result;
           *devices_result = devices;
@@ -228,7 +229,7 @@
   wait_loop[0].Run();
   EXPECT_TRUE(test_flags[0].picker_created);
   EXPECT_TRUE(test_flags[0].picker_deleted);
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
   EXPECT_EQ(blink::MEDIA_DISPLAY_VIDEO_CAPTURE, devices[0].type);
 
@@ -237,7 +238,7 @@
   EXPECT_FALSE(test_flags[1].picker_deleted);
   wait_loop[1].Run();
   EXPECT_TRUE(test_flags[1].picker_deleted);
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
   EXPECT_EQ(blink::MEDIA_DISPLAY_VIDEO_CAPTURE, devices[0].type);
   EXPECT_FALSE(devices[0].IsSameDevice(first_device));
diff --git a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
index f7620cb..98bc28ca 100644
--- a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
+++ b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
@@ -36,6 +36,7 @@
 #include "extensions/common/constants.h"
 #include "media/base/media_switches.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 #if !defined(OS_ANDROID)
 #include "chrome/browser/media/webrtc/display_media_access_handler.h"
@@ -184,8 +185,9 @@
   // bypassing blink side checks.
   if (request.video_type == blink::MEDIA_DISPLAY_VIDEO_CAPTURE &&
       !base::FeatureList::IsEnabled(blink::features::kRTCGetDisplayMedia)) {
-    std::move(callback).Run(blink::MediaStreamDevices(),
-                            blink::MEDIA_DEVICE_NOT_SUPPORTED, nullptr);
+    std::move(callback).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED, nullptr);
     return;
   }
 
@@ -200,7 +202,8 @@
     }
   }
   std::move(callback).Run(blink::MediaStreamDevices(),
-                          blink::MEDIA_DEVICE_NOT_SUPPORTED, nullptr);
+                          blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED,
+                          nullptr);
 }
 
 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
diff --git a/chrome/browser/media/webrtc/media_stream_devices_controller.cc b/chrome/browser/media/webrtc/media_stream_devices_controller.cc
index 5ed6e16..4b32fc62 100644
--- a/chrome/browser/media/webrtc/media_stream_devices_controller.cc
+++ b/chrome/browser/media/webrtc/media_stream_devices_controller.cc
@@ -110,9 +110,10 @@
       request.render_process_id, request.render_frame_id);
   // The RFH may have been destroyed by the time the request is processed.
   if (!rfh) {
-    std::move(callback).Run(blink::MediaStreamDevices(),
-                            blink::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN,
-                            std::unique_ptr<content::MediaStreamUI>());
+    std::move(callback).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN,
+        std::unique_ptr<content::MediaStreamUI>());
     return;
   }
   content::WebContents* web_contents =
@@ -135,7 +136,8 @@
             CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, rfh,
             request.security_origin);
     if (permission_status.content_setting == CONTENT_SETTING_BLOCK) {
-      controller->denial_reason_ = blink::MEDIA_DEVICE_PERMISSION_DENIED;
+      controller->denial_reason_ =
+          blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
       controller->RunCallback(permission_status.source ==
                               PermissionStatusSource::FEATURE_POLICY);
       return;
@@ -151,7 +153,8 @@
             CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, rfh,
             request.security_origin);
     if (permission_status.content_setting == CONTENT_SETTING_BLOCK) {
-      controller->denial_reason_ = blink::MEDIA_DEVICE_PERMISSION_DENIED;
+      controller->denial_reason_ =
+          blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
       controller->RunCallback(permission_status.source ==
                               PermissionStatusSource::FEATURE_POLICY);
       return;
@@ -262,9 +265,10 @@
 
 MediaStreamDevicesController::~MediaStreamDevicesController() {
   if (!callback_.is_null()) {
-    std::move(callback_).Run(blink::MediaStreamDevices(),
-                             blink::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN,
-                             std::unique_ptr<content::MediaStreamUI>());
+    std::move(callback_).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN,
+        std::unique_ptr<content::MediaStreamUI>());
   }
 }
 
@@ -291,9 +295,11 @@
 
   for (ContentSetting response : responses) {
     if (response == CONTENT_SETTING_BLOCK)
-      denial_reason_ = blink::MEDIA_DEVICE_PERMISSION_DENIED;
+      denial_reason_ =
+          blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
     else if (response == CONTENT_SETTING_ASK)
-      denial_reason_ = blink::MEDIA_DEVICE_PERMISSION_DISMISSED;
+      denial_reason_ =
+          blink::mojom::MediaStreamRequestResult::PERMISSION_DISMISSED;
   }
 
   RunCallback(blocked_by_feature_policy);
@@ -312,7 +318,7 @@
   profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext());
   content_settings_ = TabSpecificContentSettings::FromWebContents(web_contents);
 
-  denial_reason_ = blink::MEDIA_DEVICE_OK;
+  denial_reason_ = blink::mojom::MediaStreamRequestResult::OK;
   audio_setting_ = GetContentSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
                                      request, &denial_reason_);
   video_setting_ = GetContentSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
@@ -425,7 +431,8 @@
 
   // If the kill switch is, or the request was blocked because of feature
   // policy we don't update the tab context.
-  if (denial_reason_ != blink::MEDIA_DEVICE_KILL_SWITCH_ON &&
+  if (denial_reason_ !=
+          blink::mojom::MediaStreamRequestResult::KILL_SWITCH_ON &&
       !blocked_by_feature_policy) {
     UpdateTabSpecificContentSettings(audio_setting_, video_setting_);
   }
@@ -434,7 +441,8 @@
 
   // If all requested permissions are allowed then the callback should report
   // success, otherwise we report |denial_reason_|.
-  blink::MediaStreamRequestResult request_result = blink::MEDIA_DEVICE_OK;
+  blink::mojom::MediaStreamRequestResult request_result =
+      blink::mojom::MediaStreamRequestResult::OK;
   if ((audio_setting_ == CONTENT_SETTING_ALLOW ||
        audio_setting_ == CONTENT_SETTING_DEFAULT) &&
       (video_setting_ == CONTENT_SETTING_ALLOW ||
@@ -443,10 +451,10 @@
     if (devices.empty()) {
       // Even if all requested permissions are allowed, if there are no devices
       // at this point we still report a failure.
-      request_result = blink::MEDIA_DEVICE_NO_HARDWARE;
+      request_result = blink::mojom::MediaStreamRequestResult::NO_HARDWARE;
     }
   } else {
-    DCHECK_NE(blink::MEDIA_DEVICE_OK, denial_reason_);
+    DCHECK_NE(blink::mojom::MediaStreamRequestResult::OK, denial_reason_);
     request_result = denial_reason_;
   }
 
@@ -510,7 +518,7 @@
 ContentSetting MediaStreamDevicesController::GetContentSetting(
     ContentSettingsType content_type,
     const content::MediaStreamRequest& request,
-    blink::MediaStreamRequestResult* denial_reason) const {
+    blink::mojom::MediaStreamRequestResult* denial_reason) const {
   DCHECK(content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
          content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
   DCHECK(!request_.security_origin.is_empty());
@@ -527,19 +535,19 @@
   else
     device_id = request.requested_video_device_id;
   if (!HasAvailableDevices(content_type, device_id)) {
-    *denial_reason = blink::MEDIA_DEVICE_NO_HARDWARE;
+    *denial_reason = blink::mojom::MediaStreamRequestResult::NO_HARDWARE;
     return CONTENT_SETTING_BLOCK;
   }
 
   if (!IsUserAcceptAllowed(content_type)) {
-    *denial_reason = blink::MEDIA_DEVICE_PERMISSION_DENIED;
+    *denial_reason = blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
     return CONTENT_SETTING_BLOCK;
   }
 
   // Don't request if the kill switch is on.
   if (PermissionIsBlockedForReason(content_type,
                                    PermissionStatusSource::KILL_SWITCH)) {
-    *denial_reason = blink::MEDIA_DEVICE_KILL_SWITCH_ON;
+    *denial_reason = blink::mojom::MediaStreamRequestResult::KILL_SWITCH_ON;
     return CONTENT_SETTING_BLOCK;
   }
 
diff --git a/chrome/browser/media/webrtc/media_stream_devices_controller.h b/chrome/browser/media/webrtc/media_stream_devices_controller.h
index 1b2ba15..4b5920a 100644
--- a/chrome/browser/media/webrtc/media_stream_devices_controller.h
+++ b/chrome/browser/media/webrtc/media_stream_devices_controller.h
@@ -14,6 +14,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "content/public/browser/media_stream_request.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 class MediaStreamDevicesController;
 class Profile;
@@ -97,7 +98,7 @@
   ContentSetting GetContentSetting(
       ContentSettingsType content_type,
       const content::MediaStreamRequest& request,
-      blink::MediaStreamRequestResult* denial_reason) const;
+      blink::mojom::MediaStreamRequestResult* denial_reason) const;
 
   // Returns true if clicking allow on the dialog should give access to the
   // requested devices.
@@ -110,7 +111,7 @@
   // through the lifetime of the request.
   ContentSetting audio_setting_;
   ContentSetting video_setting_;
-  blink::MediaStreamRequestResult denial_reason_;
+  blink::mojom::MediaStreamRequestResult denial_reason_;
 
   content::WebContents* web_contents_;
 
diff --git a/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc b/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
index 1acd030..b88815e 100644
--- a/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
+++ b/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
@@ -42,11 +42,13 @@
   MediaStreamDevicesControllerTest()
       : example_audio_id_("fake_audio_dev"),
         example_video_id_("fake_video_dev"),
-        media_stream_result_(blink::NUM_MEDIA_REQUEST_RESULTS) {}
+        media_stream_result_(
+            blink::mojom::MediaStreamRequestResult::NUM_MEDIA_REQUEST_RESULTS) {
+  }
 
   // Dummy callback for when we deny the current request directly.
   void OnMediaStreamResponse(const blink::MediaStreamDevices& devices,
-                             blink::MediaStreamRequestResult result,
+                             blink::mojom::MediaStreamRequestResult result,
                              std::unique_ptr<content::MediaStreamUI> ui) {
     media_stream_devices_ = devices;
     media_stream_result_ = result;
@@ -67,7 +69,7 @@
   const std::string& example_audio_id() const { return example_audio_id_; }
   const std::string& example_video_id() const { return example_video_id_; }
 
-  blink::MediaStreamRequestResult media_stream_result() const {
+  blink::mojom::MediaStreamRequestResult media_stream_result() const {
     return media_stream_result_;
   }
 
@@ -181,7 +183,8 @@
 
     // Cleanup.
     media_stream_devices_.clear();
-    media_stream_result_ = blink::NUM_MEDIA_REQUEST_RESULTS;
+    media_stream_result_ =
+        blink::mojom::MediaStreamRequestResult::NUM_MEDIA_REQUEST_RESULTS;
 
     blink::MediaStreamDevices audio_devices;
     blink::MediaStreamDevice fake_audio_device(
@@ -211,7 +214,7 @@
   const std::string example_video_id_;
 
   blink::MediaStreamDevices media_stream_devices_;
-  blink::MediaStreamRequestResult media_stream_result_;
+  blink::mojom::MediaStreamRequestResult media_stream_result_;
 
   base::Closure quit_closure_;
 
@@ -637,10 +640,10 @@
 
   // The expected media stream result after clicking accept/deny for the given
   // inputs.
-  blink::MediaStreamRequestResult ExpectedMediaStreamResult() const {
+  blink::mojom::MediaStreamRequestResult ExpectedMediaStreamResult() const {
     if (ExpectMicAllowed() && ExpectCamAllowed())
-      return blink::MEDIA_DEVICE_OK;
-    return blink::MEDIA_DEVICE_PERMISSION_DENIED;
+      return blink::mojom::MediaStreamRequestResult::OK;
+    return blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
   }
 };
 
@@ -718,7 +721,7 @@
 
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::MEDIA_DEVICE_OK, media_stream_result());
+  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
   ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
   ASSERT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
 }
@@ -742,7 +745,7 @@
       PermissionRequestType::PERMISSION_MEDIASTREAM_MIC));
 
   // Accept the prompt.
-  ASSERT_EQ(blink::MEDIA_DEVICE_OK, media_stream_result());
+  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
   ASSERT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
   ASSERT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
 
@@ -754,7 +757,7 @@
                  base::Unretained(this)));
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::MEDIA_DEVICE_OK, media_stream_result());
+  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
   ASSERT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
   ASSERT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
 }
@@ -773,7 +776,8 @@
                  base::Unretained(this)));
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::MEDIA_DEVICE_PERMISSION_DENIED, media_stream_result());
+  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+            media_stream_result());
   ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
   ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
 }
@@ -795,7 +799,8 @@
                  base::Unretained(this)));
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN, media_stream_result());
+  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN,
+            media_stream_result());
   ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
   ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
 }
@@ -826,7 +831,8 @@
 
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::MEDIA_DEVICE_KILL_SWITCH_ON, media_stream_result());
+  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::KILL_SWITCH_ON,
+            media_stream_result());
   ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
   ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
 }
@@ -858,7 +864,8 @@
 
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::MEDIA_DEVICE_PERMISSION_DENIED, media_stream_result());
+  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+            media_stream_result());
   ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
   ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
   EXPECT_EQ(TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED,
@@ -892,7 +899,8 @@
 
   ASSERT_EQ(0, prompt_factory()->TotalRequestCount());
 
-  ASSERT_EQ(blink::MEDIA_DEVICE_PERMISSION_DENIED, media_stream_result());
+  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+            media_stream_result());
   ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
   ASSERT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
   EXPECT_EQ(TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED,
@@ -910,7 +918,7 @@
       base::BindOnce(&MediaStreamDevicesControllerTest::OnMediaStreamResponse,
                      base::Unretained(this)));
 
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, media_stream_result());
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
   EXPECT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
   EXPECT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
 }
@@ -926,7 +934,7 @@
       base::BindOnce(&MediaStreamDevicesControllerTest::OnMediaStreamResponse,
                      base::Unretained(this)));
 
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, media_stream_result());
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, media_stream_result());
   EXPECT_FALSE(CheckDevicesListContains(blink::MEDIA_DEVICE_AUDIO_CAPTURE));
   EXPECT_TRUE(CheckDevicesListContains(blink::MEDIA_DEVICE_VIDEO_CAPTURE));
 }
diff --git a/chrome/browser/media/webrtc/media_stream_infobar_browsertest.cc b/chrome/browser/media/webrtc/media_stream_infobar_browsertest.cc
index 18c0887..651c802 100644
--- a/chrome/browser/media/webrtc/media_stream_infobar_browsertest.cc
+++ b/chrome/browser/media/webrtc/media_stream_infobar_browsertest.cc
@@ -28,6 +28,7 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 // MediaStreamPermissionTest ---------------------------------------------------
 
@@ -75,7 +76,7 @@
   // Dummy callback for when we deny the current request directly.
   static void OnMediaStreamResponse(
       const blink::MediaStreamDevices& devices,
-      blink::MediaStreamRequestResult result,
+      blink::mojom::MediaStreamRequestResult result,
       std::unique_ptr<content::MediaStreamUI> ui) {}
 
   DISALLOW_COPY_AND_ASSIGN(MediaStreamPermissionTest);
diff --git a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
index 1996384..5e50258 100644
--- a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
+++ b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
@@ -43,7 +43,7 @@
 
 using RepeatingMediaResponseCallback =
     base::RepeatingCallback<void(const blink::MediaStreamDevices& devices,
-                                 blink::MediaStreamRequestResult result,
+                                 blink::mojom::MediaStreamRequestResult result,
                                  std::unique_ptr<content::MediaStreamUI> ui)>;
 
 #if defined(OS_MACOSX)
@@ -127,8 +127,9 @@
           chrome::android::kUserMediaScreenCapturing)) {
     // If screen capturing isn't enabled on Android, we'll use "invalid state"
     // as result, same as on desktop.
-    std::move(callback).Run(blink::MediaStreamDevices(),
-                            blink::MEDIA_DEVICE_INVALID_STATE, nullptr);
+    std::move(callback).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::INVALID_STATE, nullptr);
     return;
   }
 #endif  // defined(OS_ANDROID)
@@ -209,7 +210,7 @@
     content::WebContents* web_contents,
     int request_id,
     const blink::MediaStreamDevices& devices,
-    blink::MediaStreamRequestResult result,
+    blink::mojom::MediaStreamRequestResult result,
     std::unique_ptr<content::MediaStreamUI> ui) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
@@ -228,12 +229,12 @@
   if (request_it == requests_map.end())
     return;
 
-  blink::MediaStreamRequestResult final_result = result;
+  blink::mojom::MediaStreamRequestResult final_result = result;
 
 #if defined(OS_MACOSX)
   // If the request was approved, ask for system permissions if needed, and run
   // this function again when done.
-  if (result == blink::MEDIA_DEVICE_OK) {
+  if (result == blink::mojom::MediaStreamRequestResult::OK) {
     const content::MediaStreamRequest& request = request_it->second.request;
     if (request.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) {
       const SystemPermission system_audio_permission =
@@ -253,7 +254,8 @@
         return;
       } else if (system_audio_permission == SystemPermission::kRestricted ||
                  system_audio_permission == SystemPermission::kDenied) {
-        final_result = blink::MEDIA_DEVICE_SYSTEM_PERMISSION_DENIED;
+        final_result =
+            blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED;
         system_media_permissions::SystemAudioCapturePermissionBlocked();
       }
     }
@@ -275,7 +277,8 @@
         return;
       } else if (system_video_permission == SystemPermission::kRestricted ||
                  system_video_permission == SystemPermission::kDenied) {
-        final_result = blink::MEDIA_DEVICE_SYSTEM_PERMISSION_DENIED;
+        final_result =
+            blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED;
         system_media_permissions::SystemVideoCapturePermissionBlocked();
       }
     }
diff --git a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h
index 18a0d3a..96ee52c 100644
--- a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h
+++ b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h
@@ -11,6 +11,7 @@
 #include "chrome/browser/media/media_access_handler.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 // MediaAccessHandler for permission bubble requests.
 class PermissionBubbleMediaAccessHandler
@@ -48,7 +49,7 @@
   void OnAccessRequestResponse(content::WebContents* web_contents,
                                int request_id,
                                const blink::MediaStreamDevices& devices,
-                               blink::MediaStreamRequestResult result,
+                               blink::mojom::MediaStreamRequestResult result,
                                std::unique_ptr<content::MediaStreamUI> ui);
 
   // content::NotificationObserver implementation.
diff --git a/chrome/browser/media/webrtc/screen_capture_infobar_delegate_android.cc b/chrome/browser/media/webrtc/screen_capture_infobar_delegate_android.cc
index 40e2ea55..1e92045 100644
--- a/chrome/browser/media/webrtc/screen_capture_infobar_delegate_android.cc
+++ b/chrome/browser/media/webrtc/screen_capture_infobar_delegate_android.cc
@@ -44,9 +44,10 @@
 
 ScreenCaptureInfoBarDelegateAndroid::~ScreenCaptureInfoBarDelegateAndroid() {
   if (!callback_.is_null()) {
-    std::move(callback_).Run(blink::MediaStreamDevices(),
-                             blink::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN,
-                             nullptr);
+    std::move(callback_).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN,
+        nullptr);
   }
 }
 
@@ -72,26 +73,26 @@
 }
 
 bool ScreenCaptureInfoBarDelegateAndroid::Accept() {
-  RunCallback(blink::MEDIA_DEVICE_OK);
+  RunCallback(blink::mojom::MediaStreamRequestResult::OK);
   return true;
 }
 
 bool ScreenCaptureInfoBarDelegateAndroid::Cancel() {
-  RunCallback(blink::MEDIA_DEVICE_PERMISSION_DENIED);
+  RunCallback(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED);
   return true;
 }
 
 void ScreenCaptureInfoBarDelegateAndroid::InfoBarDismissed() {
-  RunCallback(blink::MEDIA_DEVICE_PERMISSION_DISMISSED);
+  RunCallback(blink::mojom::MediaStreamRequestResult::PERMISSION_DISMISSED);
 }
 
 void ScreenCaptureInfoBarDelegateAndroid::RunCallback(
-    blink::MediaStreamRequestResult result) {
+    blink::mojom::MediaStreamRequestResult result) {
   DCHECK(!callback_.is_null());
 
   blink::MediaStreamDevices devices;
   std::unique_ptr<content::MediaStreamUI> ui;
-  if (result == blink::MEDIA_DEVICE_OK) {
+  if (result == blink::mojom::MediaStreamRequestResult::OK) {
     content::DesktopMediaID screen_id = content::DesktopMediaID(
         content::DesktopMediaID::TYPE_SCREEN, webrtc::kFullDesktopScreenId);
     devices.push_back(
diff --git a/chrome/browser/media/webrtc/screen_capture_infobar_delegate_android.h b/chrome/browser/media/webrtc/screen_capture_infobar_delegate_android.h
index 98c8a18..829212ce 100644
--- a/chrome/browser/media/webrtc/screen_capture_infobar_delegate_android.h
+++ b/chrome/browser/media/webrtc/screen_capture_infobar_delegate_android.h
@@ -7,6 +7,7 @@
 
 #include "chrome/browser/media/media_access_handler.h"
 #include "components/infobars/core/confirm_infobar_delegate.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 namespace content {
 class WebContents;
@@ -39,7 +40,7 @@
 
   // Runs |callback_|, passing it the |result|, and (if permission was granted)
   // the appropriate stream device and UI object for video capture.
-  void RunCallback(blink::MediaStreamRequestResult result);
+  void RunCallback(blink::mojom::MediaStreamRequestResult result);
 
   content::WebContents* web_contents_;
   const content::MediaStreamRequest request_;
diff --git a/chrome/browser/media/webrtc/tab_capture_access_handler.cc b/chrome/browser/media/webrtc/tab_capture_access_handler.cc
index 963bc1c3..5e326502 100644
--- a/chrome/browser/media/webrtc/tab_capture_access_handler.cc
+++ b/chrome/browser/media/webrtc/tab_capture_access_handler.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/common/permissions/permissions_data.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 TabCaptureAccessHandler::TabCaptureAccessHandler() {
 }
@@ -49,8 +50,9 @@
       extensions::TabCaptureRegistry::Get(profile);
   if (!tab_capture_registry) {
     NOTREACHED();
-    std::move(callback).Run(devices, blink::MEDIA_DEVICE_INVALID_STATE,
-                            std::move(ui));
+    std::move(callback).Run(
+        devices, blink::mojom::MediaStreamRequestResult::INVALID_STATE,
+        std::move(ui));
     return;
   }
   // |extension| may be null if the tabCapture starts with
@@ -78,8 +80,9 @@
              ->RegisterMediaStream(web_contents, devices);
   }
   UpdateExtensionTrusted(request, extension);
-  std::move(callback).Run(devices,
-                          devices.empty() ? blink::MEDIA_DEVICE_INVALID_STATE
-                                          : blink::MEDIA_DEVICE_OK,
-                          std::move(ui));
+  std::move(callback).Run(
+      devices,
+      devices.empty() ? blink::mojom::MediaStreamRequestResult::INVALID_STATE
+                      : blink::mojom::MediaStreamRequestResult::OK,
+      std::move(ui));
 }
diff --git a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc
index 8daf0beba..cb067e7 100644
--- a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc
+++ b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc
@@ -13,7 +13,6 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_utils.h"
-#include "content/public/browser/stream_info.h"
 #include "content/public/common/resource_type.h"
 #include "content/public/common/transferrable_url_loader.mojom.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.h"
@@ -121,7 +120,7 @@
           &extensions::StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent,
           extension_id, view_id, embedded, frame_tree_node_id_,
           -1 /* render_process_id */, -1 /* render_frame_id */,
-          nullptr /* stream */, std::move(transferrable_loader), response_url));
+          std::move(transferrable_loader), response_url));
 }
 
 void PluginResponseInterceptorURLLoaderThrottle::ResumeLoad() {
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index c5c61140..5c62926 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -248,6 +248,7 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/page_transition_types.h"
@@ -4555,7 +4556,7 @@
   }
 
   void Accept(const blink::MediaStreamDevices& devices,
-              blink::MediaStreamRequestResult result,
+              blink::mojom::MediaStreamRequestResult result,
               std::unique_ptr<content::MediaStreamUI> ui) {
     if (policy_value_ || request_url_allowed_via_whitelist_) {
       ASSERT_EQ(1U, devices.size());
diff --git a/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc b/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc
index d63edf4..e0de7b4e 100644
--- a/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc
+++ b/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc
@@ -148,7 +148,7 @@
 
   MockServiceIPCServer(
       ServiceIPCServer::Client* client,
-      const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
       base::WaitableEvent* shutdown_event)
       : ServiceIPCServer(client, io_task_runner, shutdown_event) {}
 
diff --git a/chrome/browser/renderer_context_menu/context_menu_content_type_factory.cc b/chrome/browser/renderer_context_menu/context_menu_content_type_factory.cc
index a65df920..d984364 100644
--- a/chrome/browser/renderer_context_menu/context_menu_content_type_factory.cc
+++ b/chrome/browser/renderer_context_menu/context_menu_content_type_factory.cc
@@ -30,11 +30,6 @@
 
 namespace {
 
-bool CheckInternalResourcesURL(const GURL& url) {
-  return url.SchemeIs(content::kChromeUIScheme) &&
-         (url.host_piece() == chrome::kChromeUISyncResourcesHost);
-}
-
 bool IsUserSessionBlocked() {
 #if defined(OS_CHROMEOS)
   if (session_manager::SessionManager::Get() &&
@@ -74,17 +69,7 @@
   if (IsUserSessionBlocked())
     return std::make_unique<NullContextMenuContentType>(web_contents, params);
 
-  std::unique_ptr<ContextMenuContentType> content_type =
-      CreateInternal(web_contents, params);
-  SetInternalResourcesURLChecker(content_type.get());
-  return content_type;
-}
-
-// static.
-void ContextMenuContentTypeFactory::SetInternalResourcesURLChecker(
-    ContextMenuContentType* content_type) {
-  content_type->set_internal_resources_url_checker(
-      base::Bind(&CheckInternalResourcesURL));
+  return CreateInternal(web_contents, params);
 }
 
 // static
diff --git a/chrome/browser/renderer_context_menu/context_menu_content_type_factory.h b/chrome/browser/renderer_context_menu/context_menu_content_type_factory.h
index e2cd9248..32445b0c 100644
--- a/chrome/browser/renderer_context_menu/context_menu_content_type_factory.h
+++ b/chrome/browser/renderer_context_menu/context_menu_content_type_factory.h
@@ -22,11 +22,6 @@
       content::WebContents* web_contents,
       const content::ContextMenuParams& params);
 
-  // Sets the chrome specific url checker for internal resources.
-  // This is exposed for tests.
-  static void SetInternalResourcesURLChecker(
-      ContextMenuContentType* content_type);
-
  private:
   ContextMenuContentTypeFactory();
   virtual ~ContextMenuContentTypeFactory();
diff --git a/chrome/browser/renderer_context_menu/context_menu_content_type_unittest.cc b/chrome/browser/renderer_context_menu/context_menu_content_type_unittest.cc
index acf283b..717ed00 100644
--- a/chrome/browser/renderer_context_menu/context_menu_content_type_unittest.cc
+++ b/chrome/browser/renderer_context_menu/context_menu_content_type_unittest.cc
@@ -19,11 +19,7 @@
   static std::unique_ptr<ContextMenuContentType> Create(
       content::WebContents* web_contents,
       const content::ContextMenuParams& params) {
-    auto content_type =
-        std::make_unique<ContextMenuContentType>(web_contents, params, true);
-    ContextMenuContentTypeFactory::SetInternalResourcesURLChecker(
-        content_type.get());
-    return content_type;
+    return std::make_unique<ContextMenuContentType>(web_contents, params, true);
   }
 };
 
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 701d2f8..31ff9d1 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -565,12 +565,6 @@
 }
 
 // static
-bool RenderViewContextMenu::IsInternalResourcesURL(const GURL& url) {
-  return url.SchemeIs(content::kChromeUIScheme) &&
-         url.host_piece() == chrome::kChromeUISyncResourcesHost;
-}
-
-// static
 void RenderViewContextMenu::AddSpellCheckServiceItem(ui::SimpleMenuModel* menu,
                                                      bool is_checked) {
   // When the Google branding experiment is enabled, we want to show an icon
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h
index 824741727..ab836b9a 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -120,7 +120,6 @@
   friend class FormatUrlForClipboardTest;
 
   static bool IsDevToolsURL(const GURL& url);
-  static bool IsInternalResourcesURL(const GURL& url);
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   static bool ExtensionContextAndPatternMatch(
       const content::ContextMenuParams& params,
diff --git a/chrome/browser/resources/app_management/browser_proxy.js b/chrome/browser/resources/app_management/browser_proxy.js
index 84c80eda..cb6dc812 100644
--- a/chrome/browser/resources/app_management/browser_proxy.js
+++ b/chrome/browser/resources/app_management/browser_proxy.js
@@ -18,6 +18,18 @@
         this.handler = new app_management.FakePageHandler(
             this.callbackRouter.createProxy());
 
+        const permissionOptions = {};
+        permissionOptions[PwaPermissionType.CONTENT_SETTINGS_TYPE_GEOLOCATION] =
+            {
+              permissionValue: TriState.kAllow,
+              isManaged: true,
+            };
+        permissionOptions[PwaPermissionType
+                              .CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA] = {
+          permissionValue: TriState.kBlock,
+          isManaged: true
+        };
+
         const /** @type {!Array<App>}*/ appList = [
           app_management.FakePageHandler.createApp(
               'blpcfgokakmgnkcojhhkbfblekacnbeo',
@@ -68,9 +80,14 @@
           app_management.FakePageHandler.createApp(
               'aapocclcgogkmnckokdopfmhonfmgok',
               {
-                title: 'Web App, policy installed',
+                title: 'Web App, policy applied',
                 type: AppType.kWeb,
-                installSource: InstallSource.kPolicy,
+                isPinned: apps.mojom.OptionalBool.kTrue,
+                isPolicyPinned: apps.mojom.OptionalBool.kTrue,
+                installSource: apps.mojom.InstallSource.kPolicy,
+                permissions:
+                    app_management.FakePageHandler.createWebPermissions(
+                        permissionOptions),
               },
               ),
         ];
diff --git a/chrome/browser/resources/app_management/fake_page_handler.js b/chrome/browser/resources/app_management/fake_page_handler.js
index 9a06d5f..db437db1 100644
--- a/chrome/browser/resources/app_management/fake_page_handler.js
+++ b/chrome/browser/resources/app_management/fake_page_handler.js
@@ -8,9 +8,10 @@
    */
   class FakePageHandler {
     /**
+     * @param {Object=} options
      * @return {!Object<number, Permission>}
      */
-    static createWebPermissions() {
+    static createWebPermissions(options) {
       const permissionIds = [
         PwaPermissionType.CONTENT_SETTINGS_TYPE_GEOLOCATION,
         PwaPermissionType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
@@ -21,8 +22,17 @@
       const permissions = {};
 
       for (const permissionId of permissionIds) {
+        let permissionValue = TriState.kAllow;
+        let isManaged = false;
+
+        if (options && options[permissionId]) {
+          const opts = options[permissionId];
+          permissionValue = opts.permissionValue || permissionValue;
+          isManaged = opts.isManaged || isManaged;
+        }
         permissions[permissionId] = app_management.util.createPermission(
-            permissionId, PermissionValueType.kTriState, TriState.kAllow);
+            permissionId, PermissionValueType.kTriState, permissionValue,
+            isManaged);
       }
 
       return permissions;
@@ -44,7 +54,8 @@
 
       for (const permissionId of permissionIds) {
         permissions[permissionId] = app_management.util.createPermission(
-            permissionId, PermissionValueType.kBool, Bool.kTrue);
+            permissionId, PermissionValueType.kBool, Bool.kTrue,
+            false /*is_managed*/);
       }
 
       return permissions;
@@ -79,6 +90,7 @@
         version: '5.1',
         size: '9.0MB',
         isPinned: apps.mojom.OptionalBool.kFalse,
+        isPolicyPinned: apps.mojom.OptionalBool.kFalse,
         installSource: apps.mojom.InstallSource.kUser,
         permissions: {},
       };
diff --git a/chrome/browser/resources/app_management/metadata_view.html b/chrome/browser/resources/app_management/metadata_view.html
index aad0dd4f..43948ae 100644
--- a/chrome/browser/resources/app_management/metadata_view.html
+++ b/chrome/browser/resources/app_management/metadata_view.html
@@ -15,6 +15,7 @@
     }
 
     #shelf-switch {
+      align-items: center;
       display: flex;
       justify-content: space-between;
     }
@@ -36,13 +37,27 @@
       display: flex;
       justify-content: space-around;
     }
+
+    #policy-indicator {
+      fill: var(--google-grey-refresh-700);
+      margin-inline-end: 12px;
+    }
     </style>
     <template is="dom-if" if="[[pinToShelfToggleVisible_(app_)]]">
       <div id="shelf-switch-row">
         <span id="shelf-switch" class="header-text">
+          <template is="dom-if" if="[[isPolicyPinned_(app_)]]">
+            <iron-icon id="policy-indicator" icon="cr:domain" tabindex="0"
+              aria-describedby="tooltip">
+            </iron-icon>
+            <paper-tooltip id="tooltip" for="policy-indicator"
+              position="top" fit-to-visible-bounds>
+              $i18n{pinControlledByPolicy}
+            </paper-tooltip>
+          </template>
           $i18n{pinToShelf}
           <cr-toggle id="pin-to-shelf-toggle" checked="[[isPinned_(app_)]]"
-              on-change="togglePinned_">
+            on-change="togglePinned_" disabled$="[[isPolicyPinned_(app_)]]">
           </cr-toggle>
         </span>
       </div>
@@ -51,7 +66,6 @@
     <div id="metadata-overview" class="secondary-text">
       <span>[[versionString_(app_)]]</span>
       <span>[[sizeString_(app_)]]</span>
-      <!--TODO(ceciliani): Placeholder for legal declaration-->
     </div>
   </template>
   <script src="metadata_view.js"></script>
diff --git a/chrome/browser/resources/app_management/metadata_view.js b/chrome/browser/resources/app_management/metadata_view.js
index 8e460439..d063cdc 100644
--- a/chrome/browser/resources/app_management/metadata_view.js
+++ b/chrome/browser/resources/app_management/metadata_view.js
@@ -41,6 +41,10 @@
     return app.isPinned === OptionalBool.kTrue;
   },
 
+  isPolicyPinned_: function(app) {
+    return app.isPolicyPinned === OptionalBool.kTrue;
+  },
+
   /** @private */
   togglePinned_: function() {
     let newPinnedValue;
diff --git a/chrome/browser/resources/app_management/permission_toggle.html b/chrome/browser/resources/app_management/permission_toggle.html
index c7a3017..15d2a807 100644
--- a/chrome/browser/resources/app_management/permission_toggle.html
+++ b/chrome/browser/resources/app_management/permission_toggle.html
@@ -1,11 +1,34 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/policy/cr_tooltip_icon.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
 
 <dom-module id="app-management-permission-toggle">
   <template>
+    <style>
+      :host {
+        align-items: center;
+        display: flex;
+      }
+
+      #policy-indicator {
+        fill: var(--google-grey-refresh-700);
+        padding-inline-end: 12px;
+      }
+    </style>
+    <template is="dom-if"
+      if="[[isPermissionManaged_(app, permissionType)]]">
+      <iron-icon id="policy-indicator" icon="cr:domain" tabindex="0"
+        aria-describedby="tooltip">
+      </iron-icon>
+      <paper-tooltip id="tooltip" for="policy-indicator"
+        position="top" fit-to-visible-bounds>
+        $i18n{controlledByPolicy}
+      </paper-tooltip>
+    </template>
     <cr-toggle checked="[[getPermissionValueBool_(app, permissionType)]]"
-        on-change="togglePermission_">
+      on-change="togglePermission_"
+      disabled$="[[isPermissionManaged_(app, permissionType)]]">
     </cr-toggle>
   </template>
   <script src="permission_toggle.js"></script>
diff --git a/chrome/browser/resources/app_management/permission_toggle.js b/chrome/browser/resources/app_management/permission_toggle.js
index a8908cb..7856395 100644
--- a/chrome/browser/resources/app_management/permission_toggle.js
+++ b/chrome/browser/resources/app_management/permission_toggle.js
@@ -34,6 +34,22 @@
     return app_management.util.getPermissionValueBool(app, permissionType);
   },
 
+  /**
+   * @param {App} app
+   * @param {string} permissionType
+   * @return {boolean}
+   */
+  isPermissionManaged_: function(app, permissionType) {
+    if (app === undefined || permissionType === undefined) {
+      return false;
+    }
+
+    assert(app);
+
+    const permission = app_management.util.getPermission(app, permissionType);
+    assert(permission);
+    return permission.isManaged;
+  },
 
   togglePermission_: function() {
     assert(this.app);
@@ -67,8 +83,10 @@
    */
   getNewPermissionBoolean_: function(app, permissionType) {
     let newPermissionValue;
+    const currentPermission =
+        app_management.util.getPermission(app, permissionType);
 
-    switch (app_management.util.getPermission(app, permissionType).value) {
+    switch (currentPermission.value) {
       case Bool.kFalse:
         newPermissionValue = Bool.kTrue;
         break;
@@ -82,7 +100,8 @@
     assert(newPermissionValue !== undefined);
     return app_management.util.createPermission(
         app_management.util.permissionTypeHandle(app, permissionType),
-        PermissionValueType.kBool, newPermissionValue);
+        PermissionValueType.kBool, newPermissionValue,
+        currentPermission.isManaged);
   },
 
   /**
@@ -93,8 +112,10 @@
    */
   getNewPermissionTriState_: function(app, permissionType) {
     let newPermissionValue;
+    const currentPermission =
+        app_management.util.getPermission(app, permissionType);
 
-    switch (app_management.util.getPermission(app, permissionType).value) {
+    switch (currentPermission.value) {
       case TriState.kBlock:
         newPermissionValue = TriState.kAllow;
         break;
@@ -115,6 +136,7 @@
     assert(newPermissionValue !== undefined);
     return app_management.util.createPermission(
         app_management.util.permissionTypeHandle(app, permissionType),
-        PermissionValueType.kTriState, newPermissionValue);
+        PermissionValueType.kTriState, newPermissionValue,
+        currentPermission.isManaged);
   },
 });
diff --git a/chrome/browser/resources/app_management/permission_view_header.html b/chrome/browser/resources/app_management/permission_view_header.html
index f2e1358..cf696b0 100644
--- a/chrome/browser/resources/app_management/permission_view_header.html
+++ b/chrome/browser/resources/app_management/permission_view_header.html
@@ -39,8 +39,8 @@
       }
 
       #policy-indicator {
-        fill: var(--google-grey-refresh-500);
-        padding-inline-end: 10px;
+        fill: var(--google-grey-refresh-700);
+        margin-inline-end: 12px;
       }
     </style>
     <cr-icon-button class="icon-arrow-back" id="backButton"
diff --git a/chrome/browser/resources/app_management/util.js b/chrome/browser/resources/app_management/util.js
index 5330b4d..57402cb 100644
--- a/chrome/browser/resources/app_management/util.js
+++ b/chrome/browser/resources/app_management/util.js
@@ -58,13 +58,15 @@
    * @param {number} permissionId
    * @param {!PermissionValueType} valueType
    * @param {number} value
+   * @param {boolean} isManaged
    * @return {!Permission}
    */
-  function createPermission(permissionId, valueType, value) {
+  function createPermission(permissionId, valueType, value, isManaged) {
     return {
-      permissionId: permissionId,
-      valueType: valueType,
-      value: value,
+      permissionId,
+      valueType,
+      value,
+      isManaged,
     };
   }
 
diff --git a/chrome/browser/resources/chromeos/camera/src/js/metrics.js b/chrome/browser/resources/chromeos/camera/src/js/metrics.js
index 107e6c8..9368456 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/metrics.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/metrics.js
@@ -95,10 +95,12 @@
  * Returns event builder for the metrics type: capture.
  * @param {string} facingMode Camera facing-mode of the capture.
  * @param {number=} length Length of 1 minute buckets for captured video.
+ * @param {number} width The width of the capture resolution.
+ * @param {number} height The height of the capture resolution.
  * @return {analytics.EventBuilder}
  * @private
  */
-cca.metrics.captureType_ = function(facingMode, length) {
+cca.metrics.captureType_ = function(facingMode, length, [width, height]) {
   var condState = (states, cond) => {
     // Return the first existing state among the given states only if there is
     // no gate condition or the condition is met.
@@ -117,6 +119,7 @@
       .dimen(7, condState(['mic'], 'video-mode'))
       .dimen(8, condState(['max-wnd']))
       .dimen(9, condState(['tall']))
+      .dimen(10, `${width}x${height}`)
       .value(length || 0);
 };
 
diff --git a/chrome/browser/resources/chromeos/camera/src/js/util.js b/chrome/browser/resources/chromeos/camera/src/js/util.js
index 16b1e8c..386b590 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/util.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/util.js
@@ -848,7 +848,7 @@
   child.height = Math.round(scale * srcHeight);
 };
 
-/*
+/**
  * Checks if the window is maximized or fullscreen.
  * @return {boolean} True if maximized or fullscreen, false otherwise.
  */
@@ -867,7 +867,7 @@
       'https://support.google.com/chromebook/?p=camera_usage_on_chromebook');
 };
 
-/*
+/**
  * Sets up i18n messages on DOM subtree by i18n attributes.
  * @param {HTMLElement} rootElement Root of DOM subtree to be set up with.
  */
@@ -887,3 +887,18 @@
   cca.tooltip.setup(getElements('i18n-label'))
       .forEach((element) => setAriaLabel(element, 'i18n-label'));
 };
+
+/**
+ * Reads blob into Image.
+ * @param {Blob} blob
+ * @return {Promise<HTMLImageElement>}
+ * @throw {Error}
+ */
+cca.util.blobToImage = function(blob) {
+  return new Promise((resolve, reject) => {
+    const img = new Image();
+    img.onload = () => resolve(img);
+    img.onerror = () => reject(new Error('Failed to load unprocessed image'));
+    img.src = URL.createObjectURL(blob);
+  });
+};
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
index f3e65b59..51e53325 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
@@ -87,7 +87,8 @@
       this.stop_.bind(this), async (blob, isMotionPicture, filename) => {
         if (blob) {
           cca.metrics.log(
-              cca.metrics.Type.CAPTURE, this.facingMode_, blob.mins);
+              cca.metrics.Type.CAPTURE, this.facingMode_, blob.mins,
+              blob.resolution);
           try {
             await this.model_.savePicture(blob, isMotionPicture, filename);
           } catch (e) {
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
index ea3878a..03fe9e9b 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
@@ -509,6 +509,8 @@
       var recordedBlob = new Blob(
           recordedChunks, {type: cca.views.camera.Video.VIDEO_MIMETYPE});
       recordedBlob.mins = this.recordTime_.stop();
+      const {width, height} = this.stream_.getVideoTracks()[0].getSettings();
+      recordedBlob.resolution = [width, height];
       recordedChunks = [];
       if (recordedBlob.size) {
         resolve(recordedBlob);
@@ -600,7 +602,10 @@
       imageHeight: caps.imageHeight.max,
     };
   }
-  return await this.imageCapture_.takePhoto(photoSettings);
+  const blob = await this.imageCapture_.takePhoto(photoSettings);
+  const image = await cca.util.blobToImage(blob);
+  blob.resolution = [image.width, image.height];
+  return blob;
 };
 
 /**
@@ -638,29 +643,24 @@
 /**
  * Crops out maximum possible centered square from the image blob.
  * @param {Blob} blob
- * @return {Promise<Blob>} Promise with result cropped square image.
+ * @return {Blob} Promise with result cropped square image.
+ * @async
  */
-cca.views.camera.Square.prototype.cropSquare = function(blob) {
-  return new Promise((resolve, reject) => {
-    var img = new Image();
-    img.onload = () => {
-      let side = Math.min(img.width, img.height);
-      let canvas = document.createElement('canvas');
-      canvas.width = side;
-      canvas.height = side;
-      let ctx = canvas.getContext('2d');
-      ctx.drawImage(
-          img, Math.floor((img.width - side) / 2),
-          Math.floor((img.height - side) / 2), side, side, 0, 0, side, side);
-      try {
-        canvas.toBlob(resolve, 'image/jpeg');
-      } catch (e) {
-        reject(e);
-      }
-    };
-    img.onerror = () => reject(new Error('Failed to load unprocessed image'));
-    img.src = URL.createObjectURL(blob);
+cca.views.camera.Square.prototype.cropSquare = async function(blob) {
+  const img = await cca.util.blobToImage(blob);
+  let side = Math.min(img.width, img.height);
+  let canvas = document.createElement('canvas');
+  canvas.width = side;
+  canvas.height = side;
+  let ctx = canvas.getContext('2d');
+  ctx.drawImage(
+      img, Math.floor((img.width - side) / 2),
+      Math.floor((img.height - side) / 2), side, side, 0, 0, side, side);
+  const croppedBlob = await new Promise((resolve) => {
+    canvas.toBlob(resolve, 'image/jpeg');
   });
+  croppedBlob.resolution = blob.resolution;
+  return croppedBlob;
 };
 
 /**
@@ -732,6 +732,8 @@
                        'error_msg_take_photo_failed');
       throw e;
     }
+    const image = await cca.util.blobToImage(blob);
+    blob.resolution = [image.width, image.height];
     if (!playSound) {
       playSound = true;
       cca.sound.play('#sound-shutter');
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js
index 341d4670..74f20d49 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js
@@ -82,7 +82,8 @@
   /**
    * List of available video devices and width, height of its supported video
    * resolutions and photo resolutions.
-   * @type {Promise<!Array<[MediaDeviceInfo, ResolList, ResolList]>>}
+   * @type {Promise<!Array<[MediaDeviceInfo, cros.mojom.CameraFacing, ResolList,
+   *     ResolList]>>}
    * @private
    */
   this.deviceResolutions_ = null;
@@ -127,20 +128,6 @@
 };
 
 /**
- * Label of front facing camera from MediaDeviceInfo.
- * @type {string}
- * @const
- */
-cca.views.camera.Options.FRONT_CAMERA_LABEL = 'Front Camera';
-
-/**
- * Label of back facing camera from MediaDeviceInfo.
- * @type {string}
- * @const
- */
-cca.views.camera.Options.BACK_CAMERA_LABEL = 'Back Camera';
-
-/**
  * Switches to the next available camera device.
  * @private
  */
@@ -283,6 +270,7 @@
           .then((devices) => {
             return Promise.all(devices.map((d) => Promise.all([
               d,
+              cca.mojo.getCameraFacing(d.deviceId),
               cca.mojo.getPhotoResolutions(d.deviceId),
               cca.mojo.getVideoConfigs(d.deviceId)
                   .then(
@@ -304,18 +292,20 @@
     let frontSetting = null;
     let backSetting = null;
     let externalSettings = [];
-    deviceResolutions.forEach(([{deviceId, label}, photoRs, videoRs]) => {
+    deviceResolutions.forEach(([{deviceId}, facing, photoRs, videoRs]) => {
       const setting = [deviceId, photoRs, videoRs];
-      switch (label) {
-        case cca.views.camera.Options.FRONT_CAMERA_LABEL:
+      switch (facing) {
+        case cros.mojom.CameraFacing.CAMERA_FACING_FRONT:
           frontSetting = setting;
           break;
-        case cca.views.camera.Options.BACK_CAMERA_LABEL:
+        case cros.mojom.CameraFacing.CAMERA_FACING_BACK:
           backSetting = setting;
           break;
-        default:
-          // TODO(inker): Use private API to get camera facing information.
+        case cros.mojom.CameraFacing.CAMERA_FACING_EXTERNAL:
           externalSettings.push(setting);
+          break;
+        default:
+          console.error(`Ignore device of unknown facing: ${facing}`);
       }
     });
     this.photoResolPreferrer_.updateResolutions(
@@ -366,7 +356,7 @@
 cca.views.camera.Options.prototype.getDeviceResolutions =
     async function(deviceId) {
   const deviceResolutions = await this.deviceResolutions_;
-  const [, photoRs, videoRs] =
+  const [, , photoRs, videoRs] =
       deviceResolutions.find(([d]) => d.deviceId == deviceId);
   return [photoRs, videoRs];
 };
diff --git a/chrome/browser/signin/identity_manager_factory.cc b/chrome/browser/signin/identity_manager_factory.cc
index 404fda1..a6f2dfc 100644
--- a/chrome/browser/signin/identity_manager_factory.cc
+++ b/chrome/browser/signin/identity_manager_factory.cc
@@ -47,12 +47,14 @@
 // |profile|.  May return null if mutation of the signed-in state is not
 // supported on the current platform.
 std::unique_ptr<identity::PrimaryAccountMutator> BuildPrimaryAccountMutator(
+    Profile* profile,
     AccountTrackerService* account_tracker_service,
     ConcreteSigninManager* signin_manager) {
 #if !defined(OS_CHROMEOS)
   return std::make_unique<identity::PrimaryAccountMutatorImpl>(
       account_tracker_service,
-      SigninManager::FromSigninManagerBase(signin_manager));
+      SigninManager::FromSigninManagerBase(signin_manager),
+      profile->GetPrefs());
 #else
   return nullptr;
 #endif
@@ -192,7 +194,7 @@
       gaia_cookie_manager_service.get());
 
   std::unique_ptr<identity::PrimaryAccountMutator> primary_account_mutator =
-      BuildPrimaryAccountMutator(account_tracker_service.get(),
+      BuildPrimaryAccountMutator(profile, account_tracker_service.get(),
                                  signin_manager.get());
 
   std::unique_ptr<identity::AccountsMutator> accounts_mutator =
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index 6cc1ecac..d39666049 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -65,7 +65,7 @@
 constexpr char kNotificationsEnabled[] = "notifications_enabled";
 constexpr char kPackageName[] = "package_name";
 constexpr char kPackageVersion[] = "package_version";
-constexpr char kPermissions[] = "permissions";
+constexpr char kPermissionStates[] = "permission_states";
 constexpr char kSticky[] = "sticky";
 constexpr char kShortcut[] = "shortcut";
 constexpr char kShouldSync[] = "should_sync";
@@ -73,6 +73,8 @@
 constexpr char kSystem[] = "system";
 constexpr char kUninstalled[] = "uninstalled";
 constexpr char kVPNProvider[] = "vpnprovider";
+constexpr char kPermissionStateGranted[] = "granted";
+constexpr char kPermissionStateManaged[] = "managed";
 
 // Defines current version for app icons. This is used for invalidation icons in
 // case we change how app icons are produced on Android side. Can be updated in
@@ -627,7 +629,8 @@
   bool should_sync = false;
   bool system = false;
   bool vpn_provider = false;
-  base::flat_map<arc::mojom::AppPermission, bool> permissions;
+  base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
+      permissions;
 
   GetInt64FromPref(package, kLastBackupAndroidId, &last_backup_android_id);
   GetInt64FromPref(package, kLastBackupTime, &last_backup_time);
@@ -635,7 +638,7 @@
   package->GetBoolean(kShouldSync, &should_sync);
   package->GetBoolean(kSystem, &system);
   package->GetBoolean(kVPNProvider, &vpn_provider);
-  const base::Value* permission_val = package->FindKey(kPermissions);
+  const base::Value* permission_val = package->FindKey(kPermissionStates);
   if (permission_val) {
     const base::DictionaryValue* permission_dict = nullptr;
     permission_val->GetAsDictionary(&permission_dict);
@@ -647,18 +650,29 @@
       base::StringToInt64(iter.key(), &permission_type);
       DCHECK_NE(-1, permission_type);
 
-      bool value = false;
-      iter.value().GetAsBoolean(&value);
+      const base::Value& permission_state = iter.value();
 
-      arc::mojom::AppPermission permission =
-          static_cast<arc::mojom::AppPermission>(permission_type);
-      permissions.insert(std::make_pair(permission, value));
+      const base::DictionaryValue* permission_state_dict;
+      if (permission_state.GetAsDictionary(&permission_state_dict)) {
+        bool granted =
+            permission_state_dict->FindBoolKey(kPermissionStateGranted)
+                .value_or(false);
+        bool managed =
+            permission_state_dict->FindBoolKey(kPermissionStateManaged)
+                .value_or(false);
+        arc::mojom::AppPermission permission =
+            static_cast<arc::mojom::AppPermission>(permission_type);
+        permissions.emplace(permission,
+                            arc::mojom::PermissionState::New(granted, managed));
+      } else {
+        LOG(ERROR) << "Permission state was not a dictionary.";
+      }
     }
   }
 
   return std::make_unique<PackageInfo>(
       package_name, package_version, last_backup_android_id, last_backup_time,
-      should_sync, system, vpn_provider, permissions);
+      should_sync, system, vpn_provider, std::move(permissions));
 }
 
 std::vector<std::string> ArcAppListPrefs::GetAppIds() const {
@@ -1265,17 +1279,37 @@
   package_dict->SetBoolean(kSystem, package.system);
   package_dict->SetBoolean(kUninstalled, false);
   package_dict->SetBoolean(kVPNProvider, package.vpn_provider);
-  if (package.permissions.has_value()) {
-    base::DictionaryValue permission_dict;
-    for (const auto& permission : package.permissions.value()) {
-      permission_dict.SetBoolean(
+
+  base::DictionaryValue permissions_dict;
+  if (package.permission_states.has_value()) {
+    // Support new format
+    for (const auto& permission : package.permission_states.value()) {
+      base::DictionaryValue permission_state_dict;
+      permission_state_dict.SetBoolKey(kPermissionStateGranted,
+                                       permission.second->granted);
+      permission_state_dict.SetBoolKey(kPermissionStateManaged,
+                                       permission.second->managed);
+      permissions_dict.SetKey(
           base::NumberToString(static_cast<int64_t>(permission.first)),
-          permission.second);
+          std::move(permission_state_dict));
     }
-    package_dict->SetKey(kPermissions, std::move(permission_dict));
+    package_dict->SetKey(kPermissionStates, std::move(permissions_dict));
+  } else if (package.permissions.has_value()) {
+    // Support deprecated format
+    for (const auto& permission : package.permissions.value()) {
+      base::DictionaryValue permission_state_dict;
+      permission_state_dict.SetBoolKey(kPermissionStateGranted,
+                                       permission.second);
+      // Assume deprecated format is not managed.
+      permission_state_dict.SetBoolKey(kPermissionStateManaged, false);
+      permissions_dict.SetKey(
+          base::NumberToString(static_cast<int64_t>(permission.first)),
+          std::move(permission_state_dict));
+    }
+    package_dict->SetKey(kPermissionStates, std::move(permissions_dict));
   } else {
-    // Remove kPermissions from dict if there are no permissions.
-    package_dict->RemoveKey(kPermissions);
+    // Remove kPermissionStates from dict if there are no permissions.
+    package_dict->RemoveKey(kPermissionStates);
   }
 
   // TODO (crbug.com/xxxxx): Remove in M78. This is required to force updating
@@ -1923,7 +1957,8 @@
     bool should_sync,
     bool system,
     bool vpn_provider,
-    const base::flat_map<arc::mojom::AppPermission, bool>& permissions)
+    base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
+        permissions)
     : package_name(package_name),
       package_version(package_version),
       last_backup_android_id(last_backup_android_id),
@@ -1931,7 +1966,7 @@
       should_sync(should_sync),
       system(system),
       vpn_provider(vpn_provider),
-      permissions(permissions) {}
+      permissions(std::move(permissions)) {}
 
 // Need to add explicit destructor for chromium style checker error:
 // Complex class/struct needs an explicit out-of-line destructor
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
index e025b74..c673b3f 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
@@ -116,15 +116,15 @@
   };
 
   struct PackageInfo {
-    PackageInfo(
-        const std::string& package_name,
-        int32_t package_version,
-        int64_t last_backup_android_id,
-        int64_t last_backup_time,
-        bool should_sync,
-        bool system,
-        bool vpn_provider,
-        const base::flat_map<arc::mojom::AppPermission, bool>& permissions);
+    PackageInfo(const std::string& package_name,
+                int32_t package_version,
+                int64_t last_backup_android_id,
+                int64_t last_backup_time,
+                bool should_sync,
+                bool system,
+                bool vpn_provider,
+                base::flat_map<arc::mojom::AppPermission,
+                               arc::mojom::PermissionStatePtr> permissions);
     ~PackageInfo();
 
     std::string package_name;
@@ -134,8 +134,9 @@
     bool should_sync;
     bool system;
     bool vpn_provider;
-    // Maps app permission to boolean values
-    base::flat_map<arc::mojom::AppPermission, bool> permissions;
+    // Maps app permission to permission states
+    base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
+        permissions;
   };
 
   class Observer {
diff --git a/chrome/browser/ui/app_list/arc/arc_app_test.cc b/chrome/browser/ui/app_list/arc/arc_app_test.cc
index 3f0dd42..fc56a90 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_test.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_test.cc
@@ -168,27 +168,47 @@
   app.sticky = true;
   fake_default_apps_.push_back(app);
 
-  base::flat_map<arc::mojom::AppPermission, bool> permissions;
-  permissions.insert(std::make_pair(arc::mojom::AppPermission::CAMERA, 0));
+  base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
+      permissions1;
+  permissions1.emplace(arc::mojom::AppPermission::CAMERA,
+                       arc::mojom::PermissionState::New(false /* granted */,
+                                                        false /* managed */));
   fake_packages_.emplace_back(arc::mojom::ArcPackageInfo::New(
       kPackageName1 /* package_name */, 1 /* package_version */,
       1 /* last_backup_android_id */, 1 /* last_backup_time */,
       false /* sync */, false /* system */, false /* vpn_provider */,
-      nullptr /* web_app_info */, permissions));
+      nullptr /* web_app_info */, base::nullopt, std::move(permissions1)));
 
-  permissions.insert(std::make_pair(arc::mojom::AppPermission::MICROPHONE, 0));
+  base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
+      permissions2;
+  permissions2.emplace(arc::mojom::AppPermission::CAMERA,
+                       arc::mojom::PermissionState::New(false /* granted */,
+                                                        false /* managed */));
+  permissions2.emplace(arc::mojom::AppPermission::MICROPHONE,
+                       arc::mojom::PermissionState::New(false /* granted */,
+                                                        false /* managed */));
   fake_packages_.emplace_back(arc::mojom::ArcPackageInfo::New(
       kPackageName2 /* package_name */, 2 /* package_version */,
       2 /* last_backup_android_id */, 2 /* last_backup_time */, true /* sync */,
       false /* system */, false /* vpn_provider */, nullptr /* web_app_info */,
-      permissions));
+      base::nullopt, std::move(permissions2)));
 
-  permissions.insert(std::make_pair(arc::mojom::AppPermission::LOCATION, 1));
+  base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
+      permissions3;
+  permissions3.emplace(arc::mojom::AppPermission::CAMERA,
+                       arc::mojom::PermissionState::New(false /* granted */,
+                                                        false /* managed */));
+  permissions3.emplace(arc::mojom::AppPermission::MICROPHONE,
+                       arc::mojom::PermissionState::New(false /* granted */,
+                                                        false /* managed */));
+  permissions3.emplace(arc::mojom::AppPermission::LOCATION,
+                       arc::mojom::PermissionState::New(true /* granted */,
+                                                        false /* managed */));
   fake_packages_.emplace_back(arc::mojom::ArcPackageInfo::New(
       kPackageName3 /* package_name */, 3 /* package_version */,
       3 /* last_backup_android_id */, 3 /* last_backup_time */,
       false /* sync */, false /* system */, false /* vpn_provider */,
-      nullptr /* web_app_info */, permissions));
+      nullptr /* web_app_info */, base::nullopt, std::move(permissions3)));
 
   for (int i = 0; i < 3; ++i) {
     arc::mojom::ShortcutInfo shortcut_info;
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index d7c31bb..d70e101 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -372,7 +372,7 @@
                 package_info->last_backup_android_id);
       EXPECT_EQ(package->last_backup_time, package_info->last_backup_time);
       EXPECT_EQ(package->sync, package_info->should_sync);
-      EXPECT_EQ(package->permissions, package_info->permissions);
+      EXPECT_EQ(package->permission_states, package_info->permissions);
     }
   }
 
@@ -466,13 +466,19 @@
   arc::mojom::ArcPackageInfoPtr CreatePackageWithVersion(
       const std::string& package_name,
       int package_version) {
-    base::flat_map<arc::mojom::AppPermission, bool> permissions;
-    permissions.insert(std::make_pair(arc::mojom::AppPermission::CAMERA, 0));
-    permissions.insert(std::make_pair(arc::mojom::AppPermission::LOCATION, 1));
+    base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
+        permissions;
+    permissions.emplace(arc::mojom::AppPermission::CAMERA,
+                        arc::mojom::PermissionState::New(false /* granted */,
+                                                         false /* managed */));
+    permissions.emplace(arc::mojom::AppPermission::LOCATION,
+                        arc::mojom::PermissionState::New(true /* granted */,
+                                                         false /* managed */));
     return arc::mojom::ArcPackageInfo::New(
         package_name, package_version, 1 /* last_backup_android_id */,
         1 /* last_backup_time */, true /* sync */, false /* system */,
-        false /* vpn_provider */, nullptr /* web_app_info */, permissions);
+        false /* vpn_provider */, nullptr /* web_app_info */, base::nullopt,
+        std::move(permissions) /* permission states */);
   }
 
   void AddPackage(const arc::mojom::ArcPackageInfoPtr& package) {
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
index f50cb56..d32817a9 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
@@ -32,7 +32,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/favicon_base/favicon_request_metrics.h"
 #include "components/favicon_base/favicon_types.h"
 #include "components/feature_engagement/buildflags.h"
 #include "components/prefs/scoped_user_pref_update.h"
diff --git a/chrome/browser/ui/webui/app_management/app_management.mojom b/chrome/browser/ui/webui/app_management/app_management.mojom
index 9779a9e..a1c9e39b 100644
--- a/chrome/browser/ui/webui/app_management/app_management.mojom
+++ b/chrome/browser/ui/webui/app_management/app_management.mojom
@@ -17,6 +17,7 @@
 
   string? description;
   apps.mojom.OptionalBool is_pinned;
+  apps.mojom.OptionalBool is_policy_pinned;
   string? version;
   string? size;
   map<uint32, apps.mojom.Permission> permissions;
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index 2235200..cb4663c 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -197,6 +197,9 @@
   app->is_pinned = shelf_delegate_.IsPinned(update.AppId())
                        ? OptionalBool::kTrue
                        : OptionalBool::kFalse;
+  app->is_policy_pinned = shelf_delegate_.IsPolicyPinned(update.AppId())
+                              ? OptionalBool::kTrue
+                              : OptionalBool::kFalse;
 #endif
 
   return app;
diff --git a/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc b/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc
index 1070ace..0a3d90a6 100644
--- a/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc
@@ -8,6 +8,7 @@
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"
 #include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
 #include "chrome/services/app_service/public/mojom/types.mojom.h"
 
@@ -27,6 +28,15 @@
   return ChromeLauncherController::instance()->IsAppPinned(app_id);
 }
 
+bool AppManagementShelfDelegate::IsPolicyPinned(
+    const std::string& app_id) const {
+  auto* shelf_item =
+      ChromeLauncherController::instance()->GetItem(ash::ShelfID(app_id));
+  // If the app does not exist on the launcher, it has not been pinned by
+  // policy.
+  return shelf_item && shelf_item->pinned_by_policy;
+}
+
 void AppManagementShelfDelegate::SetPinned(const std::string& app_id,
                                            OptionalBool pinned) {
   if (pinned == OptionalBool::kTrue) {
diff --git a/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h b/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h
index 40c3ba4..6471ccc 100644
--- a/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h
+++ b/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h
@@ -23,6 +23,8 @@
   bool IsPinned(const std::string& app_id);
   void SetPinned(const std::string& app_id, apps::mojom::OptionalBool pinned);
 
+  bool IsPolicyPinned(const std::string& app_id) const;
+
  private:
   // ash::ShelfModelObserver:
   void ShelfItemAdded(int index) override;
diff --git a/chrome/browser/ui/webui/app_management/app_management_ui.cc b/chrome/browser/ui/webui/app_management/app_management_ui.cc
index fd026bd..12afa0f7 100644
--- a/chrome/browser/ui/webui/app_management/app_management_ui.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_ui.cc
@@ -34,22 +34,24 @@
       {"appNoPermission", IDS_APPLICATION_INFO_APP_NO_PERMISSIONS_TEXT},
       {"back", IDS_APP_MANAGEMENT_BACK},
       {"camera", IDS_APP_MANAGEMENT_CAMERA},
+      {"controlledByPolicy", IDS_CONTROLLED_SETTING_POLICY},
       {"lessApps", IDS_APP_MANAGEMENT_LESS_APPS},
       {"location", IDS_APP_MANAGEMENT_LOCATION},
       {"microphone", IDS_APP_MANAGEMENT_MICROPHONE},
       {"moreApps", IDS_APP_MANAGEMENT_MORE_APPS},
+      {"morePermissions", IDS_APP_MANAGEMENT_MORE_PERMISSIONS},
       {"noSearchResults", IDS_APP_MANAGEMENT_NO_RESULTS},
-      {"notificationSublabel", IDS_APP_MANAGEMENT_NOTIFICATIONS_SUBLABEL},
       {"notifications", IDS_APP_MANAGEMENT_NOTIFICATIONS},
+      {"notificationSublabel", IDS_APP_MANAGEMENT_NOTIFICATIONS_SUBLABEL},
       {"openAndroidSettings", IDS_APP_MANAGEMENT_ANDROID_SETTINGS},
       {"openExtensionsSettings", IDS_APP_MANAGEMENT_EXTENSIONS_SETTINGS},
       {"openSiteSettings", IDS_APP_MANAGEMENT_SITE_SETTING},
       {"permissions", IDS_APP_MANAGEMENT_PERMISSIONS},
-      {"morePermissions", IDS_APP_MANAGEMENT_MORE_PERMISSIONS},
-      {"thisAppCan", IDS_APP_MANAGEMENT_THIS_APP_CAN},
+      {"pinControlledByPolicy", IDS_APP_MANAGEMENT_PIN_ENFORCED_BY_POLICY},
       {"pinToShelf", IDS_APP_MANAGEMENT_PIN_TO_SHELF},
       {"searchPrompt", IDS_APP_MANAGEMENT_SEARCH_PROMPT},
       {"size", IDS_APP_MANAGEMENT_SIZE},
+      {"thisAppCan", IDS_APP_MANAGEMENT_THIS_APP_CAN},
       {"title", IDS_APP_MANAGEMENT_TITLE},
       {"uninstall", IDS_APP_MANAGEMENT_UNINSTALL},
       {"version", IDS_APP_MANAGEMENT_VERSION},
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index dab98c744..155d86d 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -415,8 +415,6 @@
     return &NewWebUI<ConstrainedWebDialogUI>;
   if (url.host_piece() == chrome::kChromeUISyncInternalsHost)
     return &NewWebUI<SyncInternalsUI>;
-  if (url.host_piece() == chrome::kChromeUISyncResourcesHost)
-    return &NewWebUI<WebDialogUI>;
   if (url.host_piece() == chrome::kChromeUITranslateInternalsHost)
     return &NewWebUI<TranslateInternalsUI>;
   if (url.host_piece() == chrome::kChromeUIUkmHost)
diff --git a/chrome/browser/ui/webui/favicon_source.h b/chrome/browser/ui/webui/favicon_source.h
index 70ecb55..2c725b7 100644
--- a/chrome/browser/ui/webui/favicon_source.h
+++ b/chrome/browser/ui/webui/favicon_source.h
@@ -13,7 +13,6 @@
 #include "base/task/cancelable_task_tracker.h"
 #include "components/favicon/core/favicon_request_handler.h"
 #include "components/favicon/core/favicon_service.h"
-#include "components/favicon_base/favicon_request_metrics.h"
 #include "content/public/browser/url_data_source.h"
 #include "ui/gfx/favicon_size.h"
 
diff --git a/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc b/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc
index 0d695cf..cdf3179 100644
--- a/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc
@@ -132,8 +132,7 @@
 }
 
 void PrivetPrinterHandler::StartLister(
-    const scoped_refptr<local_discovery::ServiceDiscoverySharedClient>&
-        client) {
+    scoped_refptr<local_discovery::ServiceDiscoverySharedClient> client) {
   DCHECK(!service_discovery_client_.get() ||
          service_discovery_client_.get() == client.get());
   service_discovery_client_ = client;
diff --git a/chrome/browser/ui/webui/print_preview/privet_printer_handler.h b/chrome/browser/ui/webui/print_preview/privet_printer_handler.h
index 5d0e7a5..5c5f6bb 100644
--- a/chrome/browser/ui/webui/print_preview/privet_printer_handler.h
+++ b/chrome/browser/ui/webui/print_preview/privet_printer_handler.h
@@ -66,8 +66,7 @@
 
  private:
   void StartLister(
-      const scoped_refptr<local_discovery::ServiceDiscoverySharedClient>&
-          client);
+      scoped_refptr<local_discovery::ServiceDiscoverySharedClient> client);
   void StopLister();
   void CapabilitiesUpdateClient(
       std::unique_ptr<cloud_print::PrivetHTTPClient> http_client);
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 4e0aca4c..1198042a 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -147,7 +147,6 @@
 const char kChromeUISyncFileSystemInternalsHost[] = "syncfs-internals";
 const char kChromeUISyncHost[] = "sync";
 const char kChromeUISyncInternalsHost[] = "sync-internals";
-const char kChromeUISyncResourcesHost[] = "syncresources";
 const char kChromeUISystemInfoHost[] = "system";
 const char kChromeUITermsHost[] = "terms";
 const char kChromeUITermsURL[] = "chrome://terms/";
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index e0eef740..bee6ec04 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -149,7 +149,6 @@
 extern const char kChromeUISyncFileSystemInternalsHost[];
 extern const char kChromeUISyncHost[];
 extern const char kChromeUISyncInternalsHost[];
-extern const char kChromeUISyncResourcesHost[];
 extern const char kChromeUISystemInfoHost[];
 extern const char kChromeUITermsHost[];
 extern const char kChromeUITermsURL[];
diff --git a/chrome/service/cloud_print/print_system_win.cc b/chrome/service/cloud_print/print_system_win.cc
index 035d36a..580982c 100644
--- a/chrome/service/cloud_print/print_system_win.cc
+++ b/chrome/service/cloud_print/print_system_win.cc
@@ -438,7 +438,7 @@
         const gfx::Rect& render_area,
         const gfx::Size& render_dpi,
         bool use_color,
-        const scoped_refptr<base::SingleThreadTaskRunner>& client_task_runner) {
+        scoped_refptr<base::SingleThreadTaskRunner> client_task_runner) {
       DCHECK(CurrentlyOnServiceIOThread());
       auto utility_host = std::make_unique<ServiceUtilityProcessHost>(
           this, client_task_runner.get());
diff --git a/chrome/services/app_service/public/mojom/types.mojom b/chrome/services/app_service/public/mojom/types.mojom
index be48fb46..38314ef5 100644
--- a/chrome/services/app_service/public/mojom/types.mojom
+++ b/chrome/services/app_service/public/mojom/types.mojom
@@ -57,6 +57,8 @@
   PermissionValueType value_type;
   // The semantics of value depends on the value_type.
   uint32 value;
+  // If the permission is managed by an enterprise policy.
+  bool is_managed;
 };
 
 // The types of apps available in the registry.
diff --git a/chrome/test/chromedriver/key_converter_unittest.cc b/chrome/test/chromedriver/key_converter_unittest.cc
index e733936..dbc2b1a 100644
--- a/chrome/test/chromedriver/key_converter_unittest.cc
+++ b/chrome/test/chromedriver/key_converter_unittest.cc
@@ -342,6 +342,7 @@
 }
 
 TEST(KeyConverter, AllSpecialWebDriverKeysOnEnglishKeyboard) {
+  ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
   const char kTextForKeys[] = {
 #if defined(OS_LINUX)
       0, 0, 0, 0, '\t', 0, '\r', '\r', 0, 0, 0, 0, 0,
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 f82537c..c118ed6c 100644
--- a/chrome/test/data/webui/app_management/app_management_browsertest.js
+++ b/chrome/test/data/webui/app_management/app_management_browsertest.js
@@ -139,3 +139,17 @@
 TEST_F('AppManagementArcPermissionViewTest', 'All', function() {
   mocha.run();
 });
+
+function AppManagementManagedAppsTest() {}
+
+AppManagementManagedAppsTest.prototype = {
+  __proto__: AppManagementBrowserTest.prototype,
+
+  extraLibraries: AppManagementBrowserTest.prototype.extraLibraries.concat([
+    'managed_apps_test.js',
+  ]),
+};
+
+TEST_F('AppManagementManagedAppsTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/app_management/arc_permission_view_test.js b/chrome/test/data/webui/app_management/arc_permission_view_test.js
index df00817..6784ac2 100644
--- a/chrome/test/data/webui/app_management/arc_permission_view_test.js
+++ b/chrome/test/data/webui/app_management/arc_permission_view_test.js
@@ -8,11 +8,6 @@
   let arcPermissionView;
   let fakeHandler;
 
-  function getPermissionItemByPermissionType(permissionType) {
-    return arcPermissionView.root.querySelector(
-        '[permission-type=' + permissionType + ']');
-  }
-
   function expandPermissions() {
     arcPermissionView.root.querySelector('#subpermission-expand-row').click();
   }
@@ -22,20 +17,13 @@
         arcPermissionView.app_, permissionType);
   }
 
-  function getPermissionToggleByType(permissionType) {
-    return arcPermissionView.root
-        .querySelector('[permission-type=' + permissionType + ']')
-        .root.querySelector('app-management-permission-toggle')
-        .root.querySelector('cr-toggle');
-  }
-
   async function clickPermissionToggle(permissionType) {
-    getPermissionToggleByType(permissionType).click();
+    getPermissionCrToggleByType(arcPermissionView, permissionType).click();
     await fakeHandler.$.flushForTesting();
   }
 
   async function clickPermissionItem(permissionType) {
-    getPermissionItemByPermissionType(permissionType).click();
+    getPermissionItemByType(arcPermissionView, permissionType).click();
     await fakeHandler.$.flushForTesting();
   }
 
@@ -71,25 +59,30 @@
 
   test('Permissions are hidden correctly', () => {
     expandPermissions();
-    assertTrue(isHidden(getPermissionItemByPermissionType('MICROPHONE')));
-    assertFalse(isHidden(getPermissionItemByPermissionType('LOCATION')));
-    assertFalse(isHidden(getPermissionItemByPermissionType('CAMERA')));
+    assertTrue(
+        isHidden(getPermissionItemByType(arcPermissionView, 'MICROPHONE')));
+    assertFalse(
+        isHidden(getPermissionItemByType(arcPermissionView, 'LOCATION')));
+    assertFalse(isHidden(getPermissionItemByType(arcPermissionView, 'CAMERA')));
   });
 
   test('Toggle works correctly', async () => {
     const checkPermissionToggle = async (permissionType) => {
       assertTrue(getPermissionBoolByType(permissionType));
-      assertTrue(getPermissionToggleByType(permissionType).checked);
+      assertTrue(getPermissionCrToggleByType(arcPermissionView, permissionType)
+                     .checked);
 
       // Toggle Off.
       await clickPermissionToggle(permissionType);
       assertFalse(getPermissionBoolByType(permissionType));
-      assertFalse(getPermissionToggleByType(permissionType).checked);
+      assertFalse(getPermissionCrToggleByType(arcPermissionView, permissionType)
+                      .checked);
 
       // Toggle On.
       await clickPermissionToggle(permissionType);
       assertTrue(getPermissionBoolByType(permissionType));
-      assertTrue(getPermissionToggleByType(permissionType).checked);
+      assertTrue(getPermissionCrToggleByType(arcPermissionView, permissionType)
+                     .checked);
     };
 
     expandPermissions();
@@ -102,17 +95,20 @@
   test('OnClick handler for permission item works correctly', async () => {
     const checkPermissionItemOnClick = async (permissionType) => {
       assertTrue(getPermissionBoolByType(permissionType));
-      assertTrue(getPermissionToggleByType(permissionType).checked);
+      assertTrue(getPermissionCrToggleByType(arcPermissionView, permissionType)
+                     .checked);
 
       // Toggle Off.
       await clickPermissionItem(permissionType);
       assertFalse(getPermissionBoolByType(permissionType));
-      assertFalse(getPermissionToggleByType(permissionType).checked);
+      assertFalse(getPermissionCrToggleByType(arcPermissionView, permissionType)
+                      .checked);
 
       // Toggle On.
       await clickPermissionItem(permissionType);
       assertTrue(getPermissionBoolByType(permissionType));
-      assertTrue(getPermissionToggleByType(permissionType).checked);
+      assertTrue(getPermissionCrToggleByType(arcPermissionView, permissionType)
+                     .checked);
     };
 
     expandPermissions();
diff --git a/chrome/test/data/webui/app_management/managed_apps_test.js b/chrome/test/data/webui/app_management/managed_apps_test.js
new file mode 100644
index 0000000..d78f054
--- /dev/null
+++ b/chrome/test/data/webui/app_management/managed_apps_test.js
@@ -0,0 +1,71 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+suite('<app-management-managed-apps>', () => {
+  let appDetailView;
+  let fakeHandler;
+
+  setup(async () => {
+    fakeHandler = setupFakeHandler();
+    replaceStore();
+
+    // Create a Web app which is installed and pinned by policy
+    // and has location set to on and camera set to off by policy.
+    const permissionOptions = {};
+    permissionOptions[PwaPermissionType.CONTENT_SETTINGS_TYPE_GEOLOCATION] = {
+      permissionValue: TriState.kAllow,
+      isManaged: true,
+    };
+    permissionOptions[PwaPermissionType
+                          .CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA] = {
+      permissionValue: TriState.kBlock,
+      isManaged: true
+    };
+    const policyAppOptions = {
+      type: apps.mojom.AppType.kWeb,
+      isPinned: apps.mojom.OptionalBool.kTrue,
+      isPolicyPinned: apps.mojom.OptionalBool.kTrue,
+      installSource: apps.mojom.InstallSource.kPolicy,
+      permissions: app_management.FakePageHandler.createWebPermissions(
+          permissionOptions),
+    };
+    const app = await fakeHandler.addApp(null, policyAppOptions);
+    // Select created app.
+    app_management.Store.getInstance().dispatch(
+        app_management.actions.changePage(PageType.DETAIL, app.id));
+    appDetailView =
+        document.createElement('app-management-pwa-permission-view');
+    replaceBody(appDetailView);
+    await PolymerTest.flushTasks();
+  });
+
+  test('Uninstall button affected by policy', () => {
+    const uninstallWrapper =
+        appDetailView.$$('app-management-permission-view-header')
+            .$$('#uninstall-wrapper');
+    expectTrue(!!uninstallWrapper.querySelector('#policy-indicator'));
+  });
+
+  test('Permission toggles affected by policy', () => {
+    function checkToggle(permissionType, policyAffected) {
+      const permissionToggle =
+          getPermissionToggleByType(appDetailView, permissionType);
+      expectTrue(permissionToggle.$$('cr-toggle').disabled === policyAffected);
+      expectTrue(!!permissionToggle.$$('#policy-indicator') === policyAffected);
+    }
+    checkToggle('CONTENT_SETTINGS_TYPE_NOTIFICATIONS', false);
+    checkToggle('CONTENT_SETTINGS_TYPE_GEOLOCATION', true);
+    checkToggle('CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA', true);
+    checkToggle('CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC', false);
+  });
+
+  test('Pin to shelf toggle effected by policy', () => {
+    const shelfSwitch =
+        appDetailView.$$('app-management-metadata-view').$$('#shelf-switch');
+    expectTrue(!!shelfSwitch.querySelector('#policy-indicator'));
+    expectTrue(shelfSwitch.querySelector('#pin-to-shelf-toggle').disabled);
+  });
+});
diff --git a/chrome/test/data/webui/app_management/test_util.js b/chrome/test/data/webui/app_management/test_util.js
index 32e3440..536bd08e 100644
--- a/chrome/test/data/webui/app_management/test_util.js
+++ b/chrome/test/data/webui/app_management/test_util.js
@@ -5,6 +5,7 @@
 'use strict';
 
 /**
+ * Create an app for testing purpose.
  * @param {string} id
  * @param {Object=} optConfig
  * @return {!App}
@@ -47,16 +48,6 @@
 }
 
 /**
- * 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
  */
@@ -79,3 +70,32 @@
   window.dispatchEvent(new CustomEvent('location-changed'));
   await PolymerTest.flushTasks();
 }
+
+/**
+ * @param {Element} element
+ * @param {Object} permissionType
+ * @return {Element}
+ */
+function getPermissionItemByType(view, permissionType) {
+  return view.root.querySelector('[permission-type=' + permissionType + ']');
+}
+
+/**
+ * @param {Element} element
+ * @param {Object} permissionType
+ * @return {Element}
+ */
+function getPermissionToggleByType(view, permissionType) {
+  return getPermissionItemByType(view, permissionType)
+      .root.querySelector('app-management-permission-toggle');
+}
+
+/**
+ * @param {Element} element
+ * @param {Object} permissionType
+ * @return {Element}
+ */
+function getPermissionCrToggleByType(view, permissionType) {
+  return getPermissionToggleByType(view, permissionType)
+      .root.querySelector('cr-toggle');
+}
diff --git a/chromecast/browser/DEPS b/chromecast/browser/DEPS
index 73d6d406..cd5ebfc 100644
--- a/chromecast/browser/DEPS
+++ b/chromecast/browser/DEPS
@@ -54,6 +54,7 @@
   "+services/service_manager/public",
   "+services/service_manager/embedder",
   "+third_party/blink/public/common",
+  "+third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h",
   "+ui/accessibility",
   "+ui/aura",
   "+ui/base",
diff --git a/chromecast/browser/cast_web_view_default.cc b/chromecast/browser/cast_web_view_default.cc
index 2d45095..180b33d 100644
--- a/chromecast/browser/cast_web_view_default.cc
+++ b/chromecast/browser/cast_web_view_default.cc
@@ -24,6 +24,7 @@
 #include "content/public/browser/render_widget_host_view.h"
 #include "ipc/ipc_message.h"
 #include "net/base/net_errors.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "url/gurl.h"
@@ -225,9 +226,10 @@
   if (!chromecast::IsFeatureEnabled(kAllowUserMediaAccess) &&
       !allow_media_access_) {
     LOG(WARNING) << __func__ << ": media access is disabled.";
-    std::move(callback).Run(blink::MediaStreamDevices(),
-                            blink::MEDIA_DEVICE_NOT_SUPPORTED,
-                            std::unique_ptr<content::MediaStreamUI>());
+    std::move(callback).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED,
+        std::unique_ptr<content::MediaStreamUI>());
     return;
   }
 
@@ -259,7 +261,7 @@
     }
   }
 
-  std::move(callback).Run(devices, blink::MEDIA_DEVICE_OK,
+  std::move(callback).Run(devices, blink::mojom::MediaStreamRequestResult::OK,
                           std::unique_ptr<content::MediaStreamUI>());
 }
 
diff --git a/components/arc/common/app.mojom b/components/arc/common/app.mojom
index a453b9d2..70e33787 100644
--- a/components/arc/common/app.mojom
+++ b/components/arc/common/app.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 43
+// Next MinVersion: 44
 
 module arc.mojom;
 
@@ -56,7 +56,11 @@
   // If non-null, signifies this package represents a web app that should be
   // installed on the browser side.
   [MinVersion=38] WebAppInfo? web_app_info;
+
+  // Deprecated in favour of |permission_states|
   [MinVersion=41] map<AppPermission, bool>? permissions;
+
+  [MinVersion=43] map<AppPermission, PermissionState>? permission_states;
 };
 
 // Describes ARC app shortcut.
diff --git a/components/arc/common/app_permissions.mojom b/components/arc/common/app_permissions.mojom
index 6c63abd..c6d65527 100644
--- a/components/arc/common/app_permissions.mojom
+++ b/components/arc/common/app_permissions.mojom
@@ -16,6 +16,11 @@
   NOTIFICATIONS   = 3, // Not a manifest permission
 };
 
+struct PermissionState {
+  bool granted;          // If the permission has been granted
+  bool managed;          // If the permission is managed by an enterprise policy
+};
+
 // An interface for Chrome to manipulate app permissions in ARC.
 // Next method ID: 2
 interface AppPermissionsInstance {
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index c506856..04146487 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -70,7 +70,11 @@
   if (!last_committed.is_empty())
     return last_committed;
 
-  return initial_url_;
+  return deeplink_url_;
+}
+
+const GURL& Controller::GetDeeplinkURL() {
+  return deeplink_url_;
 }
 
 Service* Controller::GetService() {
@@ -690,7 +694,7 @@
     return;
   }
   GetWebController()->SetCookie(
-      initial_url_.host(),
+      deeplink_url_.host(),
       base::BindOnce(&Controller::OnSetCookie,
                      // WebController is owned by Controller.
                      base::Unretained(this)));
@@ -706,7 +710,7 @@
   if (allow_autostart_) {
     SetStatusMessage(
         l10n_util::GetStringFUTF8(IDS_AUTOFILL_ASSISTANT_LOADING,
-                                  base::UTF8ToUTF16(initial_url_.host())));
+                                  base::UTF8ToUTF16(deeplink_url_.host())));
     SetProgress(kAutostartInitialProgress);
   }
   GetOrCheckScripts();
@@ -741,7 +745,7 @@
          state_ != AutofillAssistantState::STOPPED;
 }
 
-void Controller::Start(const GURL& initial_url,
+void Controller::Start(const GURL& deeplink_url,
                        std::unique_ptr<TriggerContext> trigger_context) {
   if (state_ != AutofillAssistantState::INACTIVE) {
     NOTREACHED();
@@ -749,7 +753,7 @@
   }
   trigger_context_ = std::move(trigger_context);
   InitFromParameters();
-  initial_url_ = initial_url;
+  deeplink_url_ = deeplink_url;
   EnterState(AutofillAssistantState::STARTING);
   client_->ShowUI();
   if (IsCookieExperimentEnabled()) {
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index ac418a1..7053fada 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -59,7 +59,7 @@
   bool NeedsUI() const;
 
   // Called when autofill assistant can start executing scripts.
-  void Start(const GURL& initial_url,
+  void Start(const GURL& deeplink_url,
              std::unique_ptr<TriggerContext> trigger_context);
 
   // Lets the controller know it's about to be deleted. This is normally called
@@ -69,6 +69,7 @@
   // Overrides ScriptExecutorDelegate:
   const ClientSettings& GetSettings() override;
   const GURL& GetCurrentURL() override;
+  const GURL& GetDeeplinkURL() override;
   Service* GetService() override;
   UiController* GetUiController() override;
   WebController* GetWebController() override;
@@ -236,7 +237,9 @@
   AutofillAssistantState state_ = AutofillAssistantState::INACTIVE;
 
   // The URL passed to Start(). Used only as long as there's no committed URL.
-  GURL initial_url_;
+  // Note that this is the deeplink passed by a caller and reported to the
+  // backend in an initial get action request.
+  GURL deeplink_url_;
 
   // Domain of the last URL the controller requested scripts from.
   std::string script_domain_;
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index c0e36ac..22e7bc0 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -934,4 +934,16 @@
   EXPECT_EQ(ACTION_APPLIED, processed_actions_capture[1].status());
 }
 
+TEST_F(ControllerTest, InitialDataUrlDoesNotChange) {
+  const std::string deeplink_url("http://initialurl.com/path");
+  Start(deeplink_url);
+  EXPECT_THAT(controller_->GetDeeplinkURL(), deeplink_url);
+  EXPECT_THAT(controller_->GetCurrentURL(), deeplink_url);
+
+  const std::string navigate_url("http://navigateurl.com/path");
+  SimulateNavigateToUrl(GURL(navigate_url));
+  EXPECT_THAT(controller_->GetDeeplinkURL().spec(), deeplink_url);
+  EXPECT_THAT(controller_->GetCurrentURL().spec(), navigate_url);
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.cc b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
index cf6744c..1559ec5 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.cc
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
@@ -19,6 +19,10 @@
   return current_url_;
 }
 
+const GURL& FakeScriptExecutorDelegate::GetDeeplinkURL() {
+  return current_url_;
+}
+
 Service* FakeScriptExecutorDelegate::GetService() {
   return service_;
 }
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.h b/components/autofill_assistant/browser/fake_script_executor_delegate.h
index c02e5883c..671d27d 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.h
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.h
@@ -27,6 +27,7 @@
 
   const ClientSettings& GetSettings() override;
   const GURL& GetCurrentURL() override;
+  const GURL& GetDeeplinkURL() override;
   Service* GetService() override;
   UiController* GetUiController() override;
   WebController* GetWebController() override;
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index bc248d5..06cee7c 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -104,7 +104,7 @@
 
   DVLOG(2) << "GetActions for " << delegate_->GetCurrentURL().host();
   delegate_->GetService()->GetActions(
-      script_path_, delegate_->GetCurrentURL(), delegate_->GetTriggerContext(),
+      script_path_, delegate_->GetDeeplinkURL(), delegate_->GetTriggerContext(),
       last_global_payload_, last_script_payload_,
       base::BindOnce(&ScriptExecutor::OnGetActions,
                      weak_ptr_factory_.GetWeakPtr()));
diff --git a/components/autofill_assistant/browser/script_executor_delegate.h b/components/autofill_assistant/browser/script_executor_delegate.h
index 3a46eea..7da41dd 100644
--- a/components/autofill_assistant/browser/script_executor_delegate.h
+++ b/components/autofill_assistant/browser/script_executor_delegate.h
@@ -45,6 +45,7 @@
 
   virtual const ClientSettings& GetSettings() = 0;
   virtual const GURL& GetCurrentURL() = 0;
+  virtual const GURL& GetDeeplinkURL() = 0;
   virtual Service* GetService() = 0;
   virtual UiController* GetUiController() = 0;
   virtual WebController* GetWebController() = 0;
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc
index ac0356b8..03aca28 100644
--- a/components/autofill_assistant/browser/web_controller.cc
+++ b/components/autofill_assistant/browser/web_controller.cc
@@ -53,13 +53,13 @@
 
 const char* const kScrollIntoViewScript =
     R"(function(node) {
+    node.scrollIntoViewIfNeeded();
     const rect = node.getBoundingClientRect();
     if (rect.height < window.innerHeight) {
       window.scrollBy({top: rect.top - window.innerHeight * 0.25});
     } else {
       window.scrollBy({top: rect.top});
     }
-    node.scrollIntoViewIfNeeded();
   })";
 
 const char* const kScrollIntoViewIfNeededScript =
diff --git a/components/autofill_assistant/browser/web_controller_browsertest.cc b/components/autofill_assistant/browser/web_controller_browsertest.cc
index 3c36d69..4253e4e 100644
--- a/components/autofill_assistant/browser/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web_controller_browsertest.cc
@@ -412,6 +412,44 @@
     std::move(done_callback).Run();
   }
 
+  // Scroll an element into view that's within a container element. This
+  // requires scrolling the container, then the window, to get the element to
+  // the desired y position.
+  void TestScrollIntoView(int initial_window_scroll_y,
+                          int initial_container_scroll_y) {
+    EXPECT_TRUE(content::ExecJs(
+        shell(), base::StringPrintf(
+                     R"(window.scrollTo(0, %d);
+           let container = document.querySelector("#scroll_container");
+           container.scrollTo(0, %d);)",
+                     initial_window_scroll_y, initial_container_scroll_y)));
+
+    Selector selector;
+    selector.selectors.emplace_back("#scroll_item_5");
+
+    FocusElement(selector);
+    base::ListValue eval_result = content::EvalJs(shell(), R"(
+      let item = document.querySelector("#scroll_item_5");
+      let itemRect = item.getBoundingClientRect();
+      let container = document.querySelector("#scroll_container");
+      let containerRect = container.getBoundingClientRect();
+      [itemRect.top, itemRect.bottom, window.innerHeight,
+           containerRect.top, containerRect.bottom])")
+                                      .ExtractList();
+    double top = eval_result.GetList()[0].GetDouble();
+    double bottom = eval_result.GetList()[1].GetDouble();
+    double window_height = eval_result.GetList()[2].GetDouble();
+    double container_top = eval_result.GetList()[3].GetDouble();
+    double container_bottom = eval_result.GetList()[4].GetDouble();
+
+    // Element is at the desired position. (top is relative to the viewport)
+    EXPECT_NEAR(top, window_height * 0.25, 0.5);
+
+    // Element is within the visible portion of its container.
+    EXPECT_GT(bottom, container_top);
+    EXPECT_LT(top, container_bottom);
+  }
+
  protected:
   std::unique_ptr<WebController> web_controller_;
 
@@ -814,47 +852,28 @@
   selector.selectors.emplace_back("#iframe");
   selector.selectors.emplace_back("#focus");
 
-  // The element is not visible initially.
-  const std::string checkNotVisibleScript = R"(
-      let iframe = document.querySelector("#iframe");
-      let div = iframe.contentDocument.querySelector("#focus");
-      let iframeRect = iframe.getBoundingClientRect();
-      let divRect = div.getBoundingClientRect();
-      iframeRect.y + divRect.y > window.innerHeight;
-  )";
-  EXPECT_EQ(true, content::EvalJs(shell(), checkNotVisibleScript));
-  FocusElement(selector);
-
-  // Verify that the scroll moved the div in the iframe into view.
   const std::string checkVisibleScript = R"(
-    const scrollTimeoutMs = 500;
-    var timer = null;
-
-    function check() {
       let iframe = document.querySelector("#iframe");
       let div = iframe.contentDocument.querySelector("#focus");
       let iframeRect = iframe.getBoundingClientRect();
       let divRect = div.getBoundingClientRect();
-      return iframeRect.y + divRect.y < window.innerHeight;
-    }
-    function onScrollDone() {
-      window.removeEventListener("scroll", onScroll);
-      domAutomationController.send(check());
-    }
-    function onScroll(e) {
-      if (timer != null) {
-        clearTimeout(timer);
-      }
-      timer = setTimeout(onScrollDone, scrollTimeoutMs);
-    }
-    if (check()) {
-      // Scrolling finished before this script started. Just return the result.
-      domAutomationController.send(true);
-    } else {
-      window.addEventListener("scroll", onScroll);
-    }
+      iframeRect.y + divRect.y < window.innerHeight;
   )";
-  EXPECT_EQ(true, content::EvalJsWithManualReply(shell(), checkVisibleScript));
+  EXPECT_EQ(false, content::EvalJs(shell(), checkVisibleScript));
+  FocusElement(selector);
+  EXPECT_EQ(true, content::EvalJs(shell(), checkVisibleScript));
+}
+
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
+                       FocusElementWithScrollIntoViewNeeded) {
+  TestScrollIntoView(/* initial_window_scroll_y= */ 0,
+                     /* initial_container_scroll_y=*/0);
+}
+
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
+                       FocusElementWithScrollIntoViewNotNeeded) {
+  TestScrollIntoView(/* initial_window_scroll_y= */ 0,
+                     /* initial_container_scroll_y=*/200);
 }
 
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SelectOption) {
diff --git a/components/content_settings/core/browser/host_content_settings_map.h b/components/content_settings/core/browser/host_content_settings_map.h
index db7e011..55d75b3 100644
--- a/components/content_settings/core/browser/host_content_settings_map.h
+++ b/components/content_settings/core/browser/host_content_settings_map.h
@@ -134,7 +134,7 @@
   // the |SETTING_SOURCE_WHITELIST| and the |primary_pattern| and
   // |secondary_pattern| are set to a wildcard pattern.  If there is no content
   // setting, NULL is returned and the |source| field of |info| is set to
-  // |SETTING_SOURCE_NONE|. The pattern fiels of |info| are set to empty
+  // |SETTING_SOURCE_NONE|. The pattern fields of |info| are set to empty
   // patterns.
   // May be called on any thread.
   std::unique_ptr<base::Value> GetWebsiteSetting(
diff --git a/components/dom_distiller/ios/distiller_page_ios.mm b/components/dom_distiller/ios/distiller_page_ios.mm
index 248a76e..b81c423 100644
--- a/components/dom_distiller/ios/distiller_page_ios.mm
+++ b/components/dom_distiller/ios/distiller_page_ios.mm
@@ -30,7 +30,7 @@
 
 namespace {
 
-// This is duplicated here from ios/web/web_state/ui/web_view_js_utils.mm in
+// This is duplicated here from ios/web/js_messaging/web_view_js_utils.mm in
 // order to handle numbers. The dom distiller proto expects integers and the
 // generated JSON deserializer does not accept doubles in the place of ints.
 // However WKWebView only returns "numbers." However, here the proto expects
diff --git a/components/favicon/core/favicon_request_handler.cc b/components/favicon/core/favicon_request_handler.cc
index 177188f..b9fe07dc 100644
--- a/components/favicon/core/favicon_request_handler.cc
+++ b/components/favicon/core/favicon_request_handler.cc
@@ -9,6 +9,8 @@
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
 #include "components/favicon/core/favicon_server_fetcher_params.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/favicon/core/features.h"
@@ -20,6 +22,52 @@
 
 namespace {
 
+void RecordFaviconAvailabilityMetric(FaviconRequestOrigin origin,
+                                     FaviconAvailability availability) {
+  switch (origin) {
+    case FaviconRequestOrigin::HISTORY:
+      UMA_HISTOGRAM_ENUMERATION("Sync.FaviconAvailability.HISTORY",
+                                availability);
+      break;
+    case FaviconRequestOrigin::HISTORY_SYNCED_TABS:
+      UMA_HISTOGRAM_ENUMERATION("Sync.FaviconAvailability.SYNCED_TABS",
+                                availability);
+      break;
+    case FaviconRequestOrigin::RECENTLY_CLOSED_TABS:
+      UMA_HISTOGRAM_ENUMERATION("Sync.FaviconAvailability.RECENTLY_CLOSED_TABS",
+                                availability);
+      break;
+    case FaviconRequestOrigin::UNKNOWN:
+      UMA_HISTOGRAM_ENUMERATION("Sync.FaviconAvailability.UNKNOWN",
+                                availability);
+      break;
+  }
+}
+
+void RecordFaviconServerGroupingMetric(FaviconRequestOrigin origin,
+                                       int group_size) {
+  DCHECK_GE(group_size, 0);
+  switch (origin) {
+    case FaviconRequestOrigin::HISTORY:
+      base::UmaHistogramCounts100(
+          "Sync.SizeOfFaviconServerRequestGroup.HISTORY", group_size);
+      break;
+    case FaviconRequestOrigin::HISTORY_SYNCED_TABS:
+      base::UmaHistogramCounts100(
+          "Sync.SizeOfFaviconServerRequestGroup.SYNCED_TABS", group_size);
+      break;
+    case FaviconRequestOrigin::RECENTLY_CLOSED_TABS:
+      base::UmaHistogramCounts100(
+          "Sync.SizeOfFaviconServerRequestGroup.RECENTLY_CLOSED_TABS",
+          group_size);
+      break;
+    case FaviconRequestOrigin::UNKNOWN:
+      base::UmaHistogramCounts100(
+          "Sync.SizeOfFaviconServerRequestGroup.UNKNOWN", group_size);
+      break;
+  }
+}
+
 // Parameter used for local bitmap queries by page url. The url is an origin,
 // and it may not have had a favicon associated with it. A trickier case is when
 // it only has domain-scoped cookies, but visitors are redirected to HTTPS on
diff --git a/components/favicon/core/favicon_request_handler.h b/components/favicon/core/favicon_request_handler.h
index c60e9d6..1833ef40 100644
--- a/components/favicon/core/favicon_request_handler.h
+++ b/components/favicon/core/favicon_request_handler.h
@@ -12,7 +12,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "components/favicon_base/favicon_callback.h"
-#include "components/favicon_base/favicon_request_metrics.h"
 #include "url/gurl.h"
 
 namespace favicon {
@@ -20,6 +19,29 @@
 class FaviconService;
 class LargeIconService;
 
+// The UI origin of an icon request.
+enum class FaviconRequestOrigin {
+  // Unknown origin.
+  UNKNOWN,
+  // chrome://history.
+  HISTORY,
+  // chrome://history/syncedTabs.
+  HISTORY_SYNCED_TABS,
+  // Recently closed tabs menu.
+  RECENTLY_CLOSED_TABS,
+};
+
+// Where the icon sent in the response is coming from. Used for metrics.
+enum class FaviconAvailability {
+  // Icon recovered from local storage (but may originally come from server).
+  kLocal = 0,
+  // Icon recovered using sync.
+  kSync = 1,
+  // Icon not found.
+  kNotAvailable = 2,
+  kMaxValue = kNotAvailable,
+};
+
 // Class for handling favicon requests by page url, forwarding them to local
 // storage, sync or Google server accordingly.
 // TODO(victorvianna): Refactor LargeIconService to avoid having to pass both
diff --git a/components/favicon/core/favicon_request_handler_unittest.cc b/components/favicon/core/favicon_request_handler_unittest.cc
index 7595101..c83f832 100644
--- a/components/favicon/core/favicon_request_handler_unittest.cc
+++ b/components/favicon/core/favicon_request_handler_unittest.cc
@@ -13,7 +13,6 @@
 #include "components/favicon/core/features.h"
 #include "components/favicon/core/large_icon_service.h"
 #include "components/favicon/core/test/mock_favicon_service.h"
-#include "components/favicon_base/favicon_request_metrics.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/favicon_base/BUILD.gn b/components/favicon_base/BUILD.gn
index 2e559e2..f1057b17 100644
--- a/components/favicon_base/BUILD.gn
+++ b/components/favicon_base/BUILD.gn
@@ -11,8 +11,6 @@
     "fallback_icon_style.cc",
     "fallback_icon_style.h",
     "favicon_callback.h",
-    "favicon_request_metrics.cc",
-    "favicon_request_metrics.h",
     "favicon_types.cc",
     "favicon_types.h",
     "favicon_url_parser.cc",
diff --git a/components/favicon_base/favicon_request_metrics.cc b/components/favicon_base/favicon_request_metrics.cc
deleted file mode 100644
index 4e3d1527..0000000
--- a/components/favicon_base/favicon_request_metrics.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/favicon_base/favicon_request_metrics.h"
-
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-
-void favicon::RecordFaviconAvailabilityMetric(
-    favicon::FaviconRequestOrigin origin,
-    favicon::FaviconAvailability availability) {
-  switch (origin) {
-    case favicon::FaviconRequestOrigin::HISTORY:
-      UMA_HISTOGRAM_ENUMERATION("Sync.FaviconAvailability.HISTORY",
-                                availability);
-      break;
-    case favicon::FaviconRequestOrigin::HISTORY_SYNCED_TABS:
-      UMA_HISTOGRAM_ENUMERATION("Sync.FaviconAvailability.SYNCED_TABS",
-                                availability);
-      break;
-    case favicon::FaviconRequestOrigin::RECENTLY_CLOSED_TABS:
-      UMA_HISTOGRAM_ENUMERATION("Sync.FaviconAvailability.RECENTLY_CLOSED_TABS",
-                                availability);
-      break;
-    case favicon::FaviconRequestOrigin::UNKNOWN:
-      UMA_HISTOGRAM_ENUMERATION("Sync.FaviconAvailability.UNKNOWN",
-                                availability);
-      break;
-  }
-}
-
-void favicon::RecordFaviconServerGroupingMetric(
-    favicon::FaviconRequestOrigin origin,
-    int group_size) {
-  DCHECK_GE(group_size, 0);
-  switch (origin) {
-    case favicon::FaviconRequestOrigin::HISTORY:
-      base::UmaHistogramCounts100(
-          "Sync.SizeOfFaviconServerRequestGroup.HISTORY", group_size);
-      break;
-    case favicon::FaviconRequestOrigin::HISTORY_SYNCED_TABS:
-      base::UmaHistogramCounts100(
-          "Sync.SizeOfFaviconServerRequestGroup.SYNCED_TABS", group_size);
-      break;
-    case favicon::FaviconRequestOrigin::RECENTLY_CLOSED_TABS:
-      base::UmaHistogramCounts100(
-          "Sync.SizeOfFaviconServerRequestGroup.RECENTLY_CLOSED_TABS",
-          group_size);
-      break;
-    case favicon::FaviconRequestOrigin::UNKNOWN:
-      base::UmaHistogramCounts100(
-          "Sync.SizeOfFaviconServerRequestGroup.UNKNOWN", group_size);
-      break;
-  }
-}
diff --git a/components/favicon_base/favicon_request_metrics.h b/components/favicon_base/favicon_request_metrics.h
deleted file mode 100644
index f02a978..0000000
--- a/components/favicon_base/favicon_request_metrics.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_FAVICON_BASE_FAVICON_REQUEST_METRICS_H_
-#define COMPONENTS_FAVICON_BASE_FAVICON_REQUEST_METRICS_H_
-
-// Header used for collecting metrics associated with favicon retrieval by UI.
-
-namespace favicon {
-// The UI origin of an icon request.
-enum class FaviconRequestOrigin {
-  // Unknown origin.
-  UNKNOWN,
-  // chrome://history.
-  HISTORY,
-  // chrome://history/syncedTabs.
-  HISTORY_SYNCED_TABS,
-  // Recently closed tabs menu.
-  RECENTLY_CLOSED_TABS,
-};
-
-enum class FaviconAvailability {
-  kLocal = 0,
-  kSync = 1,
-  kNotAvailable = 2,
-  kMaxValue = kNotAvailable,
-};
-
-void RecordFaviconAvailabilityMetric(FaviconRequestOrigin origin,
-                                     FaviconAvailability availability);
-
-void RecordFaviconServerGroupingMetric(FaviconRequestOrigin origin,
-                                       int group_size);
-
-}  // namespace favicon
-
-#endif  // COMPONENTS_FAVICON_BASE_FAVICON_REQUEST_METRICS_H_
diff --git a/components/offline_pages/content/background_loader/DEPS b/components/offline_pages/content/background_loader/DEPS
index b54fdf66..33226e6 100644
--- a/components/offline_pages/content/background_loader/DEPS
+++ b/components/offline_pages/content/background_loader/DEPS
@@ -2,4 +2,5 @@
   "+content/public/browser",
   "+content/public/test",
   "+content/public/common/window_container_type.mojom-shared.h",
+  "+third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h",
 ]
diff --git a/components/offline_pages/content/background_loader/background_loader_contents.cc b/components/offline_pages/content/background_loader/background_loader_contents.cc
index fba1d59..eccb73f1c 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents.cc
+++ b/components/offline_pages/content/background_loader/background_loader_contents.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "content/public/browser/web_contents.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 namespace background_loader {
 
@@ -119,7 +120,7 @@
   // No permissions granted, act as if dismissed.
   std::move(callback).Run(
       blink::MediaStreamDevices(),
-      blink::MediaStreamRequestResult::MEDIA_DEVICE_PERMISSION_DISMISSED,
+      blink::mojom::MediaStreamRequestResult::PERMISSION_DISMISSED,
       std::unique_ptr<content::MediaStreamUI>());
 }
 
diff --git a/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc b/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
index d758e02..6b28793 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
+++ b/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
@@ -11,6 +11,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/window_container_type.mojom-shared.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "url/gurl.h"
 
 namespace background_loader {
@@ -36,10 +37,12 @@
   bool can_download_delegate_called() { return delegate_called_; }
 
   void MediaAccessCallback(const blink::MediaStreamDevices& devices,
-                           blink::MediaStreamRequestResult result,
+                           blink::mojom::MediaStreamRequestResult result,
                            std::unique_ptr<content::MediaStreamUI> ui);
   blink::MediaStreamDevices devices() { return devices_; }
-  blink::MediaStreamRequestResult request_result() { return request_result_; }
+  blink::mojom::MediaStreamRequestResult request_result() {
+    return request_result_;
+  }
   content::MediaStreamUI* media_stream_ui() { return media_stream_ui_.get(); }
 
   void WaitForSignal() { waiter_.Wait(); }
@@ -49,7 +52,7 @@
   bool download_;
   bool delegate_called_;
   blink::MediaStreamDevices devices_;
-  blink::MediaStreamRequestResult request_result_;
+  blink::mojom::MediaStreamRequestResult request_result_;
   std::unique_ptr<content::MediaStreamUI> media_stream_ui_;
   base::WaitableEvent waiter_;
 };
@@ -89,7 +92,7 @@
 
 void BackgroundLoaderContentsTest::MediaAccessCallback(
     const blink::MediaStreamDevices& devices,
-    blink::MediaStreamRequestResult result,
+    blink::mojom::MediaStreamRequestResult result,
     std::unique_ptr<content::MediaStreamUI> ui) {
   devices_ = devices;
   request_result_ = result;
@@ -171,7 +174,7 @@
   // No devices allowed.
   ASSERT_TRUE(devices().empty());
   // Permission has been dismissed rather than denied.
-  ASSERT_EQ(blink::MediaStreamRequestResult::MEDIA_DEVICE_PERMISSION_DISMISSED,
+  ASSERT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DISMISSED,
             request_result());
   ASSERT_EQ(nullptr, media_stream_ui());
 }
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index 3f98b03..60d12d4 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -180,8 +180,9 @@
     return;
   }
 
-  const std::string current_url =
-      result_type_running_ == REMOTE_SEND_URL ? current_query_ : std::string();
+  const std::string current_url = result_type_running_ == DEFAULT_SERP_FOR_URL
+                                      ? current_query_
+                                      : std::string();
   // Create a request for suggestions, routing completion to
   // OnContextualSuggestionsLoaderAvailable.
   client()
@@ -349,7 +350,7 @@
 
   // When running the personalized service, we want to store suggestion
   // responses if non-empty.
-  if (result_type_running_ == REMOTE_NO_URL && !json_data.empty()) {
+  if (result_type_running_ == DEFAULT_SERP && !json_data.empty()) {
     client()->GetPrefs()->SetString(omnibox::kZeroSuggestCachedResults,
                                     json_data);
 
@@ -563,7 +564,7 @@
 }
 
 void ZeroSuggestProvider::MaybeUseCachedSuggestions() {
-  if (result_type_running_ != REMOTE_NO_URL)
+  if (result_type_running_ != DEFAULT_SERP)
     return;
 
   std::string json_data =
@@ -589,6 +590,7 @@
   const bool can_send_current_url = CanSendURL(
       current_url, suggest_url, default_provider, current_page_classification_,
       template_url_service->search_terms_data(), client());
+
   // Collect metrics on eligibility.
   GURL arbitrary_insecure_url(kArbitraryInsecureUrlString);
   ZeroSuggestEligibility eligibility = ZeroSuggestEligibility::ELIGIBLE;
@@ -612,7 +614,7 @@
 
   if (current_page_classification_ ==
       metrics::OmniboxEventProto::CHROMEOS_APP_LIST) {
-    return REMOTE_NO_URL;
+    return DEFAULT_SERP;
   }
 
   if (OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial(
@@ -621,7 +623,7 @@
                client()->GetPrefs(), client()->IsAuthenticated(),
                template_url_service)
                ? MOST_VISITED
-               : REMOTE_NO_URL;
+               : DEFAULT_SERP;
   }
 
   if (OmniboxFieldTrial::InZeroSuggestMostVisitedWithoutSerpFieldTrial(
@@ -637,5 +639,5 @@
     return MOST_VISITED;
   }
 
-  return can_send_current_url ? REMOTE_SEND_URL : NONE;
+  return can_send_current_url ? DEFAULT_SERP_FOR_URL : NONE;
 }
diff --git a/components/omnibox/browser/zero_suggest_provider.h b/components/omnibox/browser/zero_suggest_provider.h
index dbb0dfa..38e8e77 100644
--- a/components/omnibox/browser/zero_suggest_provider.h
+++ b/components/omnibox/browser/zero_suggest_provider.h
@@ -64,7 +64,6 @@
   void ResetSession() override;
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(ZeroSuggestProviderTest, TypeOfResultToRun);
   FRIEND_TEST_ALL_PREFIXES(ZeroSuggestProviderTest,
                            TestStartWillStopForSomeInput);
   ZeroSuggestProvider(AutocompleteProviderClient* client,
@@ -77,19 +76,12 @@
   // at any time.
   enum ResultType {
     NONE,
-
-    // A remote endpoint (usually the default search provider) is queried for
-    // suggestions. The endpoint is sent the user's authentication state, but
-    // not sent the current URL.
-    REMOTE_NO_URL,
-
-    // A remote endpoint (usually the default search provider) is queried for
-    // suggestions. The endpoint is sent the user's authentication state and
-    // the current URL.
-    REMOTE_SEND_URL,
-
-    // Gets the most visited sites from local history.
-    MOST_VISITED,
+    DEFAULT_SERP,          // The default search provider is queried for
+                           // zero-suggest suggestions.
+    DEFAULT_SERP_FOR_URL,  // The default search provider is queried for
+                           // zero-suggest suggestions that are specific
+                           // to the visited URL.
+    MOST_VISITED
   };
 
   // BaseSearchProvider:
diff --git a/components/omnibox/browser/zero_suggest_provider_unittest.cc b/components/omnibox/browser/zero_suggest_provider_unittest.cc
index 0b4287a..4b9b5fc 100644
--- a/components/omnibox/browser/zero_suggest_provider_unittest.cc
+++ b/components/omnibox/browser/zero_suggest_provider_unittest.cc
@@ -14,7 +14,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
-#include "build/build_config.h"
 #include "components/history/core/browser/top_sites.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/mock_autocomplete_provider_client.h"
@@ -156,7 +155,7 @@
   void SetZeroSuggestVariantForAllContexts(const std::string& variant);
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
-  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 
   std::unique_ptr<FakeAutocompleteProviderClient> client_;
   scoped_refptr<ZeroSuggestProvider> provider_;
@@ -203,55 +202,12 @@
 
 void ZeroSuggestProviderTest::SetZeroSuggestVariantForAllContexts(
     const std::string& variant) {
-  scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
-  scoped_feature_list_->InitAndEnableFeatureWithParameters(
+  scoped_feature_list_.InitAndEnableFeatureWithParameters(
       omnibox::kOnFocusSuggestions,
       {{std::string(OmniboxFieldTrial::kZeroSuggestVariantRule) + ":*:*",
         variant}});
 }
 
-TEST_F(ZeroSuggestProviderTest, TypeOfResultToRun) {
-  GURL current_url = GURL("https://example.com/");
-  GURL suggest_url = GURL("https://www.google.com/complete/?q={searchTerms}");
-
-  // Expect NONE by default if URL data collection is inactive.
-  EXPECT_CALL(*client_, IsPersonalizedUrlDataCollectionActive())
-      .WillRepeatedly(testing::Return(false));
-
-#if defined(OS_IOS) || defined(OS_ANDROID)
-  // iOS and Android both default to MOST_VISITED.
-  EXPECT_EQ(ZeroSuggestProvider::ResultType::MOST_VISITED,
-            provider_->TypeOfResultToRun(current_url, suggest_url));
-#else
-  // Expect REMOTE_SEND_URL type if client is authenticated and provides URLs.
-  EXPECT_EQ(ZeroSuggestProvider::ResultType::NONE,
-            provider_->TypeOfResultToRun(current_url, suggest_url));
-#endif
-
-  EXPECT_CALL(*client_, IsAuthenticated())
-      .WillRepeatedly(testing::Return(true));
-  EXPECT_CALL(*client_, IsPersonalizedUrlDataCollectionActive())
-      .WillRepeatedly(testing::Return(true));
-
-#if defined(OS_IOS) || defined(OS_ANDROID)
-  // iOS and Android both default to MOST_VISITED, even if authenticated.
-  EXPECT_EQ(ZeroSuggestProvider::ResultType::MOST_VISITED,
-            provider_->TypeOfResultToRun(current_url, suggest_url));
-#else
-  // Expect REMOTE_SEND_URL type if client is authenticated and provides URLs.
-  EXPECT_EQ(ZeroSuggestProvider::ResultType::REMOTE_SEND_URL,
-            provider_->TypeOfResultToRun(current_url, suggest_url));
-#endif
-
-  CreatePersonalizedFieldTrial();
-  EXPECT_EQ(ZeroSuggestProvider::ResultType::REMOTE_NO_URL,
-            provider_->TypeOfResultToRun(current_url, suggest_url));
-
-  CreateMostVisitedFieldTrial();
-  EXPECT_EQ(ZeroSuggestProvider::ResultType::MOST_VISITED,
-            provider_->TypeOfResultToRun(current_url, suggest_url));
-}
-
 TEST_F(ZeroSuggestProviderTest, TestDoesNotReturnMatchesForPrefix) {
   CreatePersonalizedFieldTrial();
 
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index d03e2fe..d2e2633 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -1488,12 +1488,45 @@
   optional string droid_guard_info = 2;
 }
 
+enum CrostiniAppType {
+  // The default terminal App.
+  CROSTINI_APP_TYPE_TERMINAL = 0;
+  // A registered interactive Crostini App which is
+  // not the default terminal app.
+  CROSTINI_APP_TYPE_INTERACTIVE = 1;
+  // Detected non-registered container applications.
+  CROSTINI_APP_TYPE_OTHER = 2;
+}
+
+message CrostiniApp {
+  // The default display name of the App.
+  optional string app_name = 1;
+  // The type of the App.
+  optional CrostiniAppType app_type = 2;
+
+  // Time stamp of last launch of the App with a three day granularity.
+  // The timestamp is milliseconds since Epoch in UTC timezone (Java time).
+  optional int64 last_launch_time_window_start_timestamp = 3;
+
+  // If available, the name of the Debian package belonging to this App.
+  optional string package_name = 4;
+  // If available, the version of the Debian package belonging to this App.
+  optional string package_version = 5;
+}
+
 message CrostiniStatus {
   // Time stamp of last launch of a Crostini app with three day granularity,
   // The timestamp is milliseconds since Epoch in UTC timezone (Java time).
   optional int64 last_launch_time_window_start_timestamp = 1;
+
   // The VM image version at the time of the last launch.
   optional string last_launch_vm_image_version = 2;
+  // The VM kernel version at the time of the last launch.
+  optional string last_launch_vm_kernel_version = 3;
+
+  // Contains information about each installed app at the time of the
+  // report.
+  repeated CrostiniApp installed_apps = 4;
 }
 
 // Report current active session (a user on one device) level status.
diff --git a/components/printing/test/print_mock_render_thread.cc b/components/printing/test/print_mock_render_thread.cc
index 9ff52bd..b7c6b5c 100644
--- a/components/printing/test/print_mock_render_thread.cc
+++ b/components/printing/test/print_mock_render_thread.cc
@@ -41,7 +41,7 @@
 }
 
 void PrintMockRenderThread::set_io_task_runner(
-    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
   io_task_runner_ = task_runner;
 }
 
diff --git a/components/printing/test/print_mock_render_thread.h b/components/printing/test/print_mock_render_thread.h
index 52632c00..c6dd0b9 100644
--- a/components/printing/test/print_mock_render_thread.h
+++ b/components/printing/test/print_mock_render_thread.h
@@ -43,7 +43,7 @@
   // The following functions are called by the test itself.
 
   void set_io_task_runner(
-      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
 #if BUILDFLAG(ENABLE_PRINTING)
   // Returns the pseudo-printer instance.
diff --git a/components/renderer_context_menu/context_menu_content_type.cc b/components/renderer_context_menu/context_menu_content_type.cc
index 83ff099a..88f916bf 100644
--- a/components/renderer_context_menu/context_menu_content_type.cc
+++ b/components/renderer_context_menu/context_menu_content_type.cc
@@ -19,10 +19,6 @@
   return url.SchemeIs(content::kChromeDevToolsScheme);
 }
 
-bool DefaultIsInternalResourcesURL(const GURL& url) {
-  return url.SchemeIs(content::kChromeUIScheme);
-}
-
 }  // namespace
 
 ContextMenuContentType::ContextMenuContentType(
@@ -31,10 +27,7 @@
     bool supports_custom_items)
     : params_(params),
       source_web_contents_(web_contents),
-      supports_custom_items_(supports_custom_items),
-      internal_resources_url_checker_(
-          base::Bind(&DefaultIsInternalResourcesURL)) {
-}
+      supports_custom_items_(supports_custom_items) {}
 
 ContextMenuContentType::~ContextMenuContentType() {
 }
@@ -91,14 +84,12 @@
       if (!is_candidate && params_.page_url.is_empty())
         DCHECK(params_.frame_url.is_empty());
 
-      return is_candidate && !params_.page_url.is_empty() &&
-          !IsInternalResourcesURL(params_.page_url);
+      return is_candidate && !params_.page_url.is_empty();
     }
 
     case ITEM_GROUP_FRAME: {
       bool page_group_supported = SupportsGroupInternal(ITEM_GROUP_PAGE);
-      return page_group_supported && !params_.frame_url.is_empty() &&
-          !IsInternalResourcesURL(params_.page_url);
+      return page_group_supported && !params_.frame_url.is_empty();
     }
 
     case ITEM_GROUP_LINK:
@@ -175,7 +166,3 @@
       return false;
   }
 }
-
-bool ContextMenuContentType::IsInternalResourcesURL(const GURL& url) {
-  return internal_resources_url_checker_.Run(url);
-}
diff --git a/components/renderer_context_menu/context_menu_content_type.h b/components/renderer_context_menu/context_menu_content_type.h
index aef21b4..696397d 100644
--- a/components/renderer_context_menu/context_menu_content_type.h
+++ b/components/renderer_context_menu/context_menu_content_type.h
@@ -5,7 +5,6 @@
 #ifndef COMPONENTS_RENDERER_CONTEXT_MENU_CONTEXT_MENU_CONTENT_TYPE_H_
 #define COMPONENTS_RENDERER_CONTEXT_MENU_CONTEXT_MENU_CONTENT_TYPE_H_
 
-#include "base/callback.h"
 #include "base/macros.h"
 #include "content/public/common/context_menu_params.h"
 #include "ui/base/models/simple_menu_model.h"
@@ -49,14 +48,6 @@
     ITEM_GROUP_PASSWORD
   };
 
-  typedef base::Callback<bool (const GURL& url)>
-      InternalResourcesURLChecker;
-
-  void set_internal_resources_url_checker(
-      const InternalResourcesURLChecker& checker) {
-    internal_resources_url_checker_ = checker;
-  }
-
   // Returns if |group| is enabled.
   virtual bool SupportsGroup(int group);
 
@@ -74,16 +65,10 @@
  private:
   bool SupportsGroupInternal(int group);
 
-  bool IsInternalResourcesURL(const GURL& url);
-
   const content::ContextMenuParams params_;
   content::WebContents* const source_web_contents_;
   const bool supports_custom_items_;
 
-  // A boolean callback to check if the url points to the internal
-  // resources.
-  InternalResourcesURLChecker internal_resources_url_checker_;
-
   DISALLOW_COPY_AND_ASSIGN(ContextMenuContentType);
 };
 
diff --git a/components/safe_browsing/db/v4_local_database_manager_unittest.cc b/components/safe_browsing/db/v4_local_database_manager_unittest.cc
index 539cba7a..db22c68 100644
--- a/components/safe_browsing/db/v4_local_database_manager_unittest.cc
+++ b/components/safe_browsing/db/v4_local_database_manager_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop_current.h"
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string_tokenizer.h"
@@ -110,7 +109,7 @@
       bool stores_available) {
     // Mimics V4Database::Create
     const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner =
-        base::MessageLoopCurrent::Get()->task_runner();
+        base::ThreadTaskRunnerHandle::Get();
     db_task_runner->PostTask(
         FROM_HERE, base::BindOnce(&FakeV4Database::CreateOnTaskRunner,
                                   db_task_runner, std::move(store_map),
diff --git a/components/security_interstitials/content/origin_policy_interstitial_page.cc b/components/security_interstitials/content/origin_policy_interstitial_page.cc
index 3697645..5008b3d 100644
--- a/components/security_interstitials/content/origin_policy_interstitial_page.cc
+++ b/components/security_interstitials/content/origin_policy_interstitial_page.cc
@@ -117,7 +117,8 @@
 }
 
 void OriginPolicyInterstitialPage::OnProceed() {
-  content::OriginPolicyAddExceptionFor(request_url());
+  content::OriginPolicyAddExceptionFor(web_contents()->GetBrowserContext(),
+                                       request_url());
   web_contents()->GetController().Reload(content::ReloadType::NORMAL, true);
 }
 
diff --git a/components/signin/core/browser/signin_manager.cc b/components/signin/core/browser/signin_manager.cc
index d97a3e1c..9de36b3 100644
--- a/components/signin/core/browser/signin_manager.cc
+++ b/components/signin/core/browser/signin_manager.cc
@@ -98,10 +98,6 @@
   return signin_allowed_.GetValue();
 }
 
-void SigninManager::SetSigninAllowed(bool allowed) {
-  signin_allowed_.SetValue(allowed);
-}
-
 void SigninManager::OnSigninAllowedPrefChanged() {
   if (!IsSigninAllowed() && IsAuthenticated()) {
     VLOG(0) << "IsSigninAllowed() set to false, signing out the user";
diff --git a/components/signin/core/browser/signin_manager.h b/components/signin/core/browser/signin_manager.h
index c70c9d9..0b31e088 100644
--- a/components/signin/core/browser/signin_manager.h
+++ b/components/signin/core/browser/signin_manager.h
@@ -75,20 +75,14 @@
   // up the corresponding account_id and gaia_id for this email.
   void SignIn(const std::string& username);
 
-  // Returns true if a signin to Chrome is allowed (by policy or pref).
-  // TODO(crbug.com/806778): this method should not be used externally,
-  // instead the value of the kSigninAllowed preference should be checked.
-  // Once all external code has been modified, this method will be removed.
-  bool IsSigninAllowed() const;
-
-  // Sets whether sign-in is allowed or not.
-  void SetSigninAllowed(bool allowed);
-
  private:
   friend class identity::IdentityManager;
   FRIEND_TEST_ALL_PREFIXES(SigninManagerTest, Prohibited);
   FRIEND_TEST_ALL_PREFIXES(SigninManagerTest, TestAlternateWildcard);
 
+  // Returns true if a signin to Chrome is allowed (by policy or pref).
+  bool IsSigninAllowed() const;
+
   // Send all observers |GoogleSigninSucceeded| notifications.
   void FireGoogleSigninSucceeded();
 
diff --git a/components/test/data/autofill_assistant/autofill_assistant_target_website.html b/components/test/data/autofill_assistant/autofill_assistant_target_website.html
index 5046b319..ee4ca7d 100644
--- a/components/test/data/autofill_assistant/autofill_assistant_target_website.html
+++ b/components/test/data/autofill_assistant/autofill_assistant_target_website.html
@@ -278,5 +278,17 @@
            flipPosition(this, point_b, point_a);">Moving Button</button>
       <br>
     </div>
+
+    <div style="height:500px"/>
+    <div id="scroll_container" style="width:100px;height:100px;overflow:auto;">
+      <div id="scroll_item_1" style="width:50px;height:50px">1</div>
+      <div id="scroll_item_2" style="width:50px;height:50px">2</div>
+      <!-- elements below this point cannot be seen except by scrolling
+           within the container. -->
+      <div id="scroll_item_3" style="width:50px;height:50px">3</div>
+      <div id="scroll_item_4" style="width:50px;height:50px">4</div>
+      <div id="scroll_item_5" style="width:50px;height:50px">5</div>
+    </div>
+    <div style="height:500px"/>
   </body>
 </html>
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index f525fc6..43c7af04 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1185,10 +1185,6 @@
     "loader/shared_cors_origin_access_list_impl.h",
     "loader/source_stream_to_data_pipe.cc",
     "loader/source_stream_to_data_pipe.h",
-    "loader/stream_resource_handler.cc",
-    "loader/stream_resource_handler.h",
-    "loader/stream_writer.cc",
-    "loader/stream_writer.h",
     "loader/throttling_resource_handler.cc",
     "loader/throttling_resource_handler.h",
     "loader/upload_data_stream_builder.cc",
@@ -1857,19 +1853,6 @@
     "storage_partition_impl.h",
     "storage_partition_impl_map.cc",
     "storage_partition_impl_map.h",
-    "streams/stream.cc",
-    "streams/stream.h",
-    "streams/stream_context.cc",
-    "streams/stream_context.h",
-    "streams/stream_handle_impl.cc",
-    "streams/stream_handle_impl.h",
-    "streams/stream_read_observer.h",
-    "streams/stream_register_observer.h",
-    "streams/stream_registry.cc",
-    "streams/stream_registry.h",
-    "streams/stream_url_request_job.cc",
-    "streams/stream_url_request_job.h",
-    "streams/stream_write_observer.h",
     "theme_helper_mac.h",
     "theme_helper_mac.mm",
     "tracing/background_memory_tracing_observer.cc",
diff --git a/content/browser/devtools/devtools_url_interceptor_request_job.cc b/content/browser/devtools/devtools_url_interceptor_request_job.cc
index bebd340..59c1e5c1 100644
--- a/content/browser/devtools/devtools_url_interceptor_request_job.cc
+++ b/content/browser/devtools/devtools_url_interceptor_request_job.cc
@@ -149,7 +149,7 @@
       resource_request_info->fetch_window_id(),
       resource_request_info->GetResourceType(),
       resource_request_info->GetPageTransition(),
-      resource_request_info->IsDownload(), resource_request_info->is_stream(),
+      resource_request_info->IsDownload(),
       resource_request_info->resource_intercept_policy(),
       resource_request_info->HasUserGesture(),
       resource_request_info->is_load_timing_enabled(),
diff --git a/content/browser/fileapi/file_system_manager_impl.h b/content/browser/fileapi/file_system_manager_impl.h
index d7b44bd..6dc0e8a 100644
--- a/content/browser/fileapi/file_system_manager_impl.h
+++ b/content/browser/fileapi/file_system_manager_impl.h
@@ -20,8 +20,6 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/shared_memory.h"
 #include "components/services/filesystem/public/interfaces/types.mojom.h"
-#include "content/browser/streams/stream.h"
-#include "content/browser/streams/stream_context.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 41c09cc8..cb48aa1 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -1218,7 +1218,6 @@
     const GlobalRequestID& request_id,
     bool is_download,
     NavigationDownloadPolicy download_policy,
-    bool is_stream,
     base::Optional<SubresourceLoaderParams> subresource_loader_params) {
   // The |loader_|'s job is finished. It must not call the NavigationRequest
   // anymore from now.
@@ -1228,7 +1227,6 @@
   is_download_ = is_download && download_policy.IsDownloadAllowed();
   if (is_download_)
     RecordDownloadUseCountersPostPolicyCheck();
-  is_stream_ = is_stream;
   request_id_ = request_id;
 
   DCHECK_EQ(state_, STARTED);
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index d56e176..05bf0830 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -456,7 +456,6 @@
       const GlobalRequestID& request_id,
       bool is_download,
       NavigationDownloadPolicy download_policy,
-      bool is_stream,
       base::Optional<SubresourceLoaderParams> subresource_loader_params)
       override;
   void OnRequestFailed(
@@ -741,7 +740,6 @@
   network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints_;
   base::Optional<net::SSLInfo> ssl_info_;
   bool is_download_ = false;
-  bool is_stream_ = false;
   GlobalRequestID request_id_;
 
   // Holds information for the navigation while the WillFailRequest
diff --git a/content/browser/frame_host/navigator.cc b/content/browser/frame_host/navigator.cc
index b1106e0..ac53e0a 100644
--- a/content/browser/frame_host/navigator.cc
+++ b/content/browser/frame_host/navigator.cc
@@ -6,7 +6,6 @@
 
 #include "base/time/time.h"
 #include "content/browser/loader/prefetched_signed_exchange_cache.h"
-#include "content/public/browser/stream_handle.h"
 
 namespace content {
 
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index 249c92d..cb6977a 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -38,7 +38,6 @@
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/browser/stream_handle.h"
 #include "content/public/common/bindings_policy.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_constants.h"
diff --git a/content/browser/frame_host/origin_policy_throttle.cc b/content/browser/frame_host/origin_policy_throttle.cc
index 68d6799a..b99786b 100644
--- a/content/browser/frame_host/origin_policy_throttle.cc
+++ b/content/browser/frame_host/origin_policy_throttle.cc
@@ -16,7 +16,6 @@
 #include "content/browser/frame_host/navigation_handle_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/origin_policy_error_reason.h"
@@ -35,20 +34,15 @@
 static constexpr const char* kDeletePolicy = "0";
 static constexpr const char* kReportTo = "report-to";
 static constexpr const char* kPolicy = "policy";
-
-// Marker for (temporarily) exempted origins.
-// TODO(vogelheim): Make sure this is outside the value space for policy
-//                  names. A name with a comma in it shouldn't be allowed, but
-//                  I don't think we presently check this anywhere.
-static constexpr const char* kExemptedOriginPolicyVersion = "exception,";
 }  // namespace
 
 namespace content {
 
 // Implement the public "API" from
 // content/public/browser/origin_policy_commands.h:
-void OriginPolicyAddExceptionFor(const GURL& url) {
-  OriginPolicyThrottle::AddExceptionFor(url);
+void OriginPolicyAddExceptionFor(BrowserContext* browser_context,
+                                 const GURL& url) {
+  OriginPolicyThrottle::AddExceptionFor(browser_context, url);
 }
 
 // static
@@ -87,8 +81,15 @@
 }
 
 // static
-void OriginPolicyThrottle::AddExceptionFor(const GURL& url) {
-  GetKnownVersions()[url::Origin::Create(url)] = kExemptedOriginPolicyVersion;
+void OriginPolicyThrottle::AddExceptionFor(BrowserContext* browser_context,
+                                           const GURL& url) {
+  DCHECK(browser_context);
+  StoragePartitionImpl* storage_partition = static_cast<StoragePartitionImpl*>(
+      BrowserContext::GetStoragePartitionForSite(browser_context, url));
+  network::mojom::OriginPolicyManager* origin_policy_manager =
+      storage_partition->GetOriginPolicyManagerForBrowserProcess();
+
+  origin_policy_manager->AddExceptionFor(url::Origin::Create(url));
 }
 
 OriginPolicyThrottle::~OriginPolicyThrottle() {}
@@ -114,80 +115,24 @@
   if (!navigation_handle()->GetResponseHeaders())
     return NavigationThrottle::PROCEED;
 
-  // TODO(andypaicu):
-  // This entire logic needs to be moved to OriginPolicyManager with the
-  // store migration.
-
-  // This determines whether and which policy version applies and fetches it.
-  //
-  // Inputs are the kSecOriginPolicy HTTP header, and the version
-  // we've last seen from this particular origin.
-  //
-  // - header with kDeletePolicy received: No policy applies, and delete the
-  //       last-known policy for this origin.
-  // - header received: Use header version and update last-known policy.
-  // - no header received, last-known version exists: Use last-known version
-  // - no header, no last-known version: No policy applies.
-
-  std::string response_version =
-      GetRequestedPolicyAndReportGroupFromHeader().policy_version;
-  bool header_found = !response_version.empty();
-
-  url::Origin origin = GetRequestOrigin();
-  DCHECK(!origin.Serialize().empty());
-  DCHECK(!origin.opaque());
-  KnownVersionMap& versions = GetKnownVersions();
-  auto iter = versions.find(origin);
-
-  // Process policy deletion first!
-  if (header_found && response_version == kDeletePolicy) {
-    if (iter != versions.end())
-      versions.erase(iter);
-    return NavigationThrottle::PROCEED;
-  }
-
-  // Process policy exceptions.
-  if (iter != versions.end() && iter->second == kExemptedOriginPolicyVersion) {
-    return NavigationThrottle::PROCEED;
-  }
-
-  // No policy applies to this request?
-  if (!header_found && iter == versions.end()) {
-    return NavigationThrottle::PROCEED;
-  }
-
   std::string header;
   navigation_handle()->GetResponseHeaders()->GetNormalizedHeader(
       net::HttpRequestHeaders::kSecOriginPolicy, &header);
 
-  if (!header_found) {
-    // TODO(andypaicu):
-    // This is an absolute hack that will go away when we move the in-memory
-    // store to the network service OriginPolicyManager. Until then, if we have
-    // a cached policy version and we receive a request with no header set, we
-    // build this artificial header to let OriginPolicyManager know where to
-    // retrieve the policy from.
-    header = base::StrCat({"policy=", iter->second});
-  } else if (iter == versions.end()) {
-    versions.insert(std::make_pair(origin, response_version));
-  } else {
-    iter->second = response_version;
-  }
-
   network::OriginPolicyManager::RetrieveOriginPolicyCallback
       origin_policy_manager_done = base::BindOnce(
           &OriginPolicyThrottle::OnOriginPolicyManagerRetrieveDone,
           base::Unretained(this));
+
   SiteInstance* site_instance = navigation_handle()->GetStartingSiteInstance();
   StoragePartitionImpl* storage_partition =
       static_cast<StoragePartitionImpl*>(BrowserContext::GetStoragePartition(
           site_instance->GetBrowserContext(), site_instance));
-
   network::mojom::OriginPolicyManager* origin_policy_manager =
       storage_partition->GetOriginPolicyManagerForBrowserProcess();
 
   origin_policy_manager->RetrieveOriginPolicy(
-      origin, header, std::move(origin_policy_manager_done));
+      GetRequestOrigin(), header, std::move(origin_policy_manager_done));
 
   return NavigationThrottle::DEFER;
 }
@@ -196,27 +141,11 @@
   return "OriginPolicyThrottle";
 }
 
-// static
-OriginPolicyThrottle::KnownVersionMap&
-OriginPolicyThrottle::GetKnownVersionsForTesting() {
-  return GetKnownVersions();
-}
-
 OriginPolicyThrottle::OriginPolicyThrottle(NavigationHandle* handle)
     : NavigationThrottle(handle) {}
 
-OriginPolicyThrottle::KnownVersionMap&
-OriginPolicyThrottle::GetKnownVersions() {
-  static base::NoDestructor<KnownVersionMap> map_instance;
-  return *map_instance;
-}
-
-OriginPolicyThrottle::PolicyVersionAndReportTo OriginPolicyThrottle::
-    GetRequestedPolicyAndReportGroupFromHeaderStringForTesting(
-        const std::string& header) {
-  return GetRequestedPolicyAndReportGroupFromHeaderString(header);
-}
-
+// TODO(andypaicu): Remove when we have moved reporting logic to the network
+// service.
 OriginPolicyThrottle::PolicyVersionAndReportTo
 OriginPolicyThrottle::GetRequestedPolicyAndReportGroupFromHeader() const {
   std::string header;
@@ -317,30 +246,38 @@
                                   const GURL& policy_url) {}
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
-bool OriginPolicyThrottle::IsExemptedForTesting(const url::Origin& origin) {
-  KnownVersionMap& versions = GetKnownVersions();
-  auto iter = versions.find(origin);
-  if (iter != versions.end())
-    return iter->second == kExemptedOriginPolicyVersion;
-
-  return false;
-}
-
 void OriginPolicyThrottle::OnOriginPolicyManagerRetrieveDone(
     const network::mojom::OriginPolicyPtr origin_policy) {
-  if (origin_policy->state != network::mojom::OriginPolicyState::kLoaded) {
-    CancelNavigation(OriginPolicyErrorReason::kCannotLoadPolicy,
-                     origin_policy->policy_url);
-    return;
-  }
+  switch (origin_policy->state) {
+    case network::mojom::OriginPolicyState::kCannotLoadPolicy:
+      // TODO(andypaicu): OriginPolicyErrorReason is obsolete and we should use
+      // network::mojom::OriginPolicyState instead.
+      CancelNavigation(OriginPolicyErrorReason::kCannotLoadPolicy,
+                       origin_policy->policy_url);
+      return;
 
-  DCHECK(origin_policy->contents);
-  // TODO(vogelheim): Determine whether we need to parse or sanity check
-  //                  the policy content at this point.
-  static_cast<NavigationHandleImpl*>(navigation_handle())
-      ->navigation_request()
-      ->SetOriginPolicy(origin_policy->contents->raw_policy);
-  Resume();
+    case network::mojom::OriginPolicyState::kInvalidRedirect:
+      // TODO(andypaicu): OriginPolicyErrorReason is obsolete and we should use
+      // network::mojom::OriginPolicyState instead.
+      CancelNavigation(OriginPolicyErrorReason::kPolicyShouldNotRedirect,
+                       origin_policy->policy_url);
+      return;
+
+    case network::mojom::OriginPolicyState::kNoPolicyApplies:
+      Resume();
+      return;
+
+    case network::mojom::OriginPolicyState::kLoaded:
+      DCHECK(origin_policy->contents);
+      static_cast<NavigationHandleImpl*>(navigation_handle())
+          ->navigation_request()
+          ->SetOriginPolicy(origin_policy->contents->raw_policy);
+      Resume();
+      return;
+
+    default:
+      NOTREACHED();
+  }
 }
 
 }  // namespace content
diff --git a/content/browser/frame_host/origin_policy_throttle.h b/content/browser/frame_host/origin_policy_throttle.h
index 1b984824..ed72e6a1 100644
--- a/content/browser/frame_host/origin_policy_throttle.h
+++ b/content/browser/frame_host/origin_policy_throttle.h
@@ -12,6 +12,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_throttle.h"
 #include "services/network/public/mojom/origin_policy_manager.mojom.h"
 
@@ -59,7 +60,7 @@
   // otherwise invalid) policy. This is meant to be called by the security
   // interstitial.
   // This will exempt the entire origin, rather than only the given URL.
-  static void AddExceptionFor(const GURL& url);
+  static void AddExceptionFor(BrowserContext* browser_context, const GURL& url);
 
   ~OriginPolicyThrottle() override;
 
@@ -70,20 +71,11 @@
   using KnownVersionMap = std::map<url::Origin, std::string>;
   static KnownVersionMap& GetKnownVersionsForTesting();
 
-  // TODO(andypaicu): Remove this when we move the store to the network
-  // service layer.
-  static PolicyVersionAndReportTo
-  GetRequestedPolicyAndReportGroupFromHeaderStringForTesting(
-      const std::string& header);
-
-  static bool IsExemptedForTesting(const url::Origin& origin);
-
  private:
   explicit OriginPolicyThrottle(NavigationHandle* handle);
 
-  static KnownVersionMap& GetKnownVersions();
-
-  // Get the policy name and the reporting group from the header string.
+  // TODO(andypaicu): Remove when we have moved reporting logic to the network
+  // service.
   PolicyVersionAndReportTo GetRequestedPolicyAndReportGroupFromHeader() const;
   static PolicyVersionAndReportTo
   GetRequestedPolicyAndReportGroupFromHeaderString(const std::string& header);
diff --git a/content/browser/frame_host/origin_policy_throttle_unittest.cc b/content/browser/frame_host/origin_policy_throttle_unittest.cc
index 6e1cc94..703a82b8 100644
--- a/content/browser/frame_host/origin_policy_throttle_unittest.cc
+++ b/content/browser/frame_host/origin_policy_throttle_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/frame_host/origin_policy_throttle.h"
 
+#include <set>
 #include <utility>
 
 #include "base/feature_list.h"
@@ -40,10 +41,8 @@
     features_.InitWithFeatureState(features::kOriginPolicy, GetParam());
 
     RenderViewHostTestHarness::SetUp();
-    OriginPolicyThrottle::GetKnownVersionsForTesting().clear();
   }
   void TearDown() override {
-    OriginPolicyThrottle::GetKnownVersionsForTesting().clear();
     nav_handle_.reset();
     RenderViewHostTestHarness::TearDown();
   }
@@ -72,20 +71,28 @@
                             const std::string& header_value,
                             RetrieveOriginPolicyCallback callback) override {
     auto result = network::mojom::OriginPolicy::New();
-    result->state = network::mojom::OriginPolicyState::kLoaded;
-    result->contents = network::mojom::OriginPolicyContents::New();
-    result->contents->raw_policy = kExampleManifestString;
-    result->policy_url = origin.GetURL();
+
+    if (origin_exceptions_.find(origin) == origin_exceptions_.end()) {
+      result->state = network::mojom::OriginPolicyState::kLoaded;
+      result->contents = network::mojom::OriginPolicyContents::New();
+      result->contents->raw_policy = kExampleManifestString;
+      result->policy_url = origin.GetURL();
+    } else {
+      result->state = network::mojom::OriginPolicyState::kNoPolicyApplies;
+      result->policy_url = origin.GetURL();
+    }
 
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(&TestOriginPolicyManager::RunCallback,
                                   base::Unretained(this), std::move(callback),
                                   std::move(result)));
   }
+
   void RunCallback(RetrieveOriginPolicyCallback callback,
                    network::mojom::OriginPolicyPtr result) {
     std::move(callback).Run(std::move(result));
   }
+
   network::mojom::OriginPolicyManagerPtr GetPtr() {
     network::mojom::OriginPolicyManagerPtr ptr;
     auto request = mojo::MakeRequest(&ptr);
@@ -95,7 +102,14 @@
 
     return ptr;
   }
+
+  void AddExceptionFor(const url::Origin& origin) override {
+    origin_exceptions_.insert(origin);
+  }
+
+ private:
   std::unique_ptr<mojo::Binding<network::mojom::OriginPolicyManager>> binding_;
+  std::set<url::Origin> origin_exceptions_;
 };
 
 INSTANTIATE_TEST_SUITE_P(OriginPolicyThrottleTests,
@@ -182,25 +196,10 @@
       ->ResetOriginPolicyManagerForBrowserProcessForTesting();
 }
 
-TEST_P(OriginPolicyThrottleTest, AddException) {
-  if (!enabled())
-    return;
-
-  GURL url("https://example.org/bla");
-  OriginPolicyThrottle::GetKnownVersionsForTesting()[url::Origin::Create(url)] =
-      "abcd";
-
-  OriginPolicyThrottle::AddExceptionFor(url);
-  EXPECT_TRUE(
-      OriginPolicyThrottle::IsExemptedForTesting(url::Origin::Create(url)));
-}
-
 TEST_P(OriginPolicyThrottleTest, AddExceptionEndToEnd) {
   if (!enabled())
     return;
 
-  OriginPolicyThrottle::AddExceptionFor(GURL("https://example.org/blubb"));
-
   // Start the navigation.
   auto navigation = NavigationSimulator::CreateBrowserInitiated(
       GURL("https://example.org/bla"), web_contents());
@@ -210,88 +209,47 @@
   EXPECT_EQ(NavigationThrottle::PROCEED,
             navigation->GetLastThrottleCheckResult().action());
 
+  // We set a test origin policy manager as during unit tests we can't reach
+  // the network service to retrieve a valid origin policy manager.
+  TestOriginPolicyManager test_origin_policy_manager;
+  test_origin_policy_manager.AddExceptionFor(
+      url::Origin::Create(GURL("https://example.org/blubb")));
+  NavigationHandleImpl* nav_handle =
+      static_cast<NavigationHandleImpl*>(navigation->GetNavigationHandle());
+  SiteInstance* site_instance = nav_handle->GetStartingSiteInstance();
+  static_cast<StoragePartitionImpl*>(
+      BrowserContext::GetStoragePartition(site_instance->GetBrowserContext(),
+                                          site_instance))
+      ->SetOriginPolicyManagerForBrowserProcessForTesting(
+          test_origin_policy_manager.GetPtr());
+
   // Fake a response with a policy header.
   const char* raw_headers =
       "HTTP/1.1 200 OK\nSec-Origin-Policy: policy=policy-1\n\n";
   auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(
       net::HttpUtil::AssembleRawHeaders(raw_headers));
-  NavigationHandleImpl* nav_handle =
-      static_cast<NavigationHandleImpl*>(navigation->GetNavigationHandle());
   nav_handle->set_response_headers_for_testing(headers);
   navigation->ReadyToCommit();
 
-  // Due to the exception, we expect the policy to not defer.
-  EXPECT_FALSE(navigation->IsDeferred());
+  // The policy manager has to be called even though this is an exception
+  // because the throttle has no way of knowing that.
+  EXPECT_TRUE(navigation->IsDeferred());
+  OriginPolicyThrottle* policy_throttle = static_cast<OriginPolicyThrottle*>(
+      nav_handle->GetDeferringThrottleForTesting());
+  EXPECT_TRUE(policy_throttle);
 
-  // Also check that the header policy did not overwrite the exemption:
-  EXPECT_TRUE(OriginPolicyThrottle::IsExemptedForTesting(
-      url::Origin::Create(GURL("https://example.org/bla"))));
-}
+  // Wait until the navigation has been allowed to proceed.
+  navigation->Wait();
 
-TEST(OriginPolicyThrottleTest, ParseHeaders) {
-  const struct {
-    const char* header;
-    const char* policy_version;
-    const char* report_to;
-  } testcases[] = {
-      // The common cases: We expect >99% of headers to look like these:
-      {"policy=policy", "policy", ""},
-      {"policy=policy, report-to=endpoint", "policy", "endpoint"},
+  // At the end of the navigation, the navigation handle should have no policy
+  // as this origin should be exempted.
+  EXPECT_EQ("",
+            nav_handle->navigation_request()->common_params().origin_policy);
 
-      // Delete a policy. This better work.
-      {"0", "0", ""},
-      {"policy=0", "0", ""},
-      {"policy=\"0\"", "0", ""},
-      {"policy=0, report-to=endpoint", "0", "endpoint"},
-
-      // Order, please!
-      {"policy=policy, report-to=endpoint", "policy", "endpoint"},
-      {"report-to=endpoint, policy=policy", "policy", "endpoint"},
-
-      // Quoting:
-      {"policy=\"policy\"", "policy", ""},
-      {"policy=\"policy\", report-to=endpoint", "policy", "endpoint"},
-      {"policy=\"policy\", report-to=\"endpoint\"", "policy", "endpoint"},
-      {"policy=policy, report-to=\"endpoint\"", "policy", "endpoint"},
-
-      // Whitespace, and funky but valid syntax:
-      {"  policy  =   policy  ", "policy", ""},
-      {" policy = \t policy ", "policy", ""},
-      {" policy \t= \t \"policy\"  ", "policy", ""},
-      {" policy = \" policy \" ", "policy", ""},
-      {" , policy = policy , report-to=endpoint , ", "policy", "endpoint"},
-
-      // Valid policy, invalid report-to:
-      {"policy=policy, report-to endpoint", "", ""},
-      {"policy=policy, report-to=here, report-to=there", "", ""},
-      {"policy=policy, \"report-to\"=endpoint", "", ""},
-
-      // Invalid policy, valid report-to:
-      {"policy=policy1, policy=policy2", "", ""},
-      {"policy, report-to=r", "", ""},
-      {"report-to=endpoint", "", "endpoint"},
-
-      // Invalid everything:
-      {"one two three", "", ""},
-      {"one, two, three", "", ""},
-      {"policy report-to=endpoint", "", ""},
-      {"policy=policy report-to=endpoint", "", ""},
-
-      // Forward compatibility, ignore unknown keywords:
-      {"policy=pol, report-to=endpoint, unknown=keyword", "pol", "endpoint"},
-      {"unknown=keyword, policy=pol, report-to=endpoint", "pol", "endpoint"},
-      {"policy=pol, unknown=keyword", "pol", ""},
-      {"policy=policy, report_to=endpoint", "policy", ""},
-      {"policy=policy, reportto=endpoint", "policy", ""},
-  };
-  for (const auto& testcase : testcases) {
-    SCOPED_TRACE(testcase.header);
-    const auto result = OriginPolicyThrottle::
-        GetRequestedPolicyAndReportGroupFromHeaderStringForTesting(
-            testcase.header);
-    EXPECT_EQ(result.policy_version, testcase.policy_version);
-    EXPECT_EQ(result.report_to, testcase.report_to);
-  }
+  static_cast<StoragePartitionImpl*>(
+      BrowserContext::GetStoragePartition(site_instance->GetBrowserContext(),
+                                          site_instance))
+      ->ResetOriginPolicyManagerForBrowserProcessForTesting();
 }
 
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_delegate.cc b/content/browser/frame_host/render_frame_host_delegate.cc
index b70f80a4..2bfbaaa 100644
--- a/content/browser/frame_host/render_frame_host_delegate.cc
+++ b/content/browser/frame_host/render_frame_host_delegate.cc
@@ -10,6 +10,7 @@
 #include "content/browser/frame_host/render_frame_host_delegate.h"
 #include "content/public/browser/file_select_listener.h"
 #include "ipc/ipc_message.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "ui/gfx/native_widget_types.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -62,7 +63,7 @@
   LOG(ERROR) << "RenderFrameHostDelegate::RequestMediaAccessPermission: "
              << "Not supported.";
   std::move(callback).Run(blink::MediaStreamDevices(),
-                          blink::MEDIA_DEVICE_NOT_SUPPORTED,
+                          blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED,
                           std::unique_ptr<MediaStreamUI>());
 }
 
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 322bfd7b..387831a4 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -142,7 +142,6 @@
 #include "content/public/browser/site_isolation_policy.h"
 #include "content/public/browser/sms_service.h"
 #include "content/public/browser/storage_partition.h"
-#include "content/public/browser/stream_handle.h"
 #include "content/public/browser/webvr_service_provider.h"
 #include "content/public/common/bindings_policy.h"
 #include "content/public/common/content_constants.h"
diff --git a/content/browser/loader/cross_site_document_resource_handler_unittest.cc b/content/browser/loader/cross_site_document_resource_handler_unittest.cc
index 2a8a7ecc..323c46f 100644
--- a/content/browser/loader/cross_site_document_resource_handler_unittest.cc
+++ b/content/browser/loader/cross_site_document_resource_handler_unittest.cc
@@ -1142,14 +1142,6 @@
     return std::make_unique<TestResourceHandler>();
   }
 
-  std::unique_ptr<ResourceHandler> MaybeInterceptAsStream(
-      net::URLRequest* request,
-      network::ResourceResponse* response,
-      std::string* payload) override {
-    NOTREACHED();
-    return std::make_unique<TestResourceHandler>();
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherHost);
 };
diff --git a/content/browser/loader/mime_sniffing_resource_handler.cc b/content/browser/loader/mime_sniffing_resource_handler.cc
index 7b73b2a..d7e76819 100644
--- a/content/browser/loader/mime_sniffing_resource_handler.cc
+++ b/content/browser/loader/mime_sniffing_resource_handler.cc
@@ -22,7 +22,6 @@
 #include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/loader/resource_request_info_impl.h"
-#include "content/browser/loader/stream_resource_handler.h"
 #include "content/browser/web_package/signed_exchange_utils.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/plugin_service.h"
@@ -525,21 +524,8 @@
     return false;
   }
 
-  if (has_plugin && plugin.type != WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN) {
+  if (has_plugin && plugin.type != WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN)
     *handled_by_plugin = true;
-    return true;
-  }
-
-  // Attempt to intercept the request as a stream.
-  std::string payload;
-  std::unique_ptr<ResourceHandler> handler(
-      host_->MaybeInterceptAsStream(request(), response_.get(), &payload));
-  if (handler) {
-    if (!CheckResponseIsNotProvisional())
-      return false;
-    *handled_by_plugin = true;
-    intercepting_handler_->UseNewHandler(std::move(handler), payload);
-  }
 #endif
   return true;
 }
diff --git a/content/browser/loader/mime_sniffing_resource_handler_unittest.cc b/content/browser/loader/mime_sniffing_resource_handler_unittest.cc
index 638890c..127fbd2 100644
--- a/content/browser/loader/mime_sniffing_resource_handler_unittest.cc
+++ b/content/browser/loader/mime_sniffing_resource_handler_unittest.cc
@@ -57,13 +57,7 @@
 
 class TestResourceDispatcherHost : public ResourceDispatcherHostImpl {
  public:
-  explicit TestResourceDispatcherHost(bool stream_has_handler)
-      : stream_has_handler_(stream_has_handler),
-        intercepted_as_stream_(false),
-        intercepted_as_stream_count_(0),
-        new_resource_handler_(nullptr) {}
-
-  bool intercepted_as_stream() const { return intercepted_as_stream_; }
+  TestResourceDispatcherHost() : new_resource_handler_(nullptr) {}
 
   std::unique_ptr<ResourceHandler> CreateResourceHandlerForDownload(
       net::URLRequest* request,
@@ -73,20 +67,6 @@
     return CreateNewResourceHandler();
   }
 
-  std::unique_ptr<ResourceHandler> MaybeInterceptAsStream(
-      net::URLRequest* request,
-      network::ResourceResponse* response,
-      std::string* payload) override {
-    intercepted_as_stream_count_++;
-    if (stream_has_handler_)
-      intercepted_as_stream_ = true;
-    return CreateNewResourceHandler();
-  }
-
-  int intercepted_as_stream_count() const {
-    return intercepted_as_stream_count_;
-  }
-
   TestResourceHandler* new_resource_handler() const {
     return new_resource_handler_;
   }
@@ -100,16 +80,6 @@
     return std::move(new_resource_handler);
   }
 
-  // Whether the URL request should be intercepted as a stream.
-  bool stream_has_handler_;
-
-  // Whether the URL request has been intercepted as a stream.
-  bool intercepted_as_stream_;
-
-  // Count of number of times MaybeInterceptAsStream function get called in a
-  // test.
-  int intercepted_as_stream_count_;
-
   // The last alternative TestResourceHandler created by this
   // TestResourceDispatcherHost.
   TestResourceHandler* new_resource_handler_;
@@ -161,9 +131,7 @@
 class MimeSniffingResourceHandlerTest : public testing::Test {
  public:
   MimeSniffingResourceHandlerTest()
-      : stream_has_handler_(false),
-        plugin_available_(false),
-        plugin_stale_(false) {}
+      : plugin_available_(false), plugin_stale_(false) {}
 
   // Tests that the MimeSniffingHandler properly sets the accept field in the
   // header. Returns the accept header value.
@@ -172,21 +140,12 @@
       ResourceType request_resource_type,
       net::URLRequest* request);
 
-  void set_stream_has_handler(bool stream_has_handler) {
-    stream_has_handler_ = stream_has_handler;
-  }
-
   void set_plugin_available(bool plugin_available) {
     plugin_available_ = plugin_available;
   }
 
   void set_plugin_stale(bool plugin_stale) { plugin_stale_ = plugin_stale; }
 
-  bool TestStreamIsIntercepted(
-      ResourceInterceptPolicy resource_intercept_policy,
-      bool must_download,
-      ResourceType request_resource_type);
-
   // Tests the operation of the MimeSniffingHandler when it needs to buffer
   // data (example case: the response is text/plain).
   void TestHandlerSniffing(bool response_started,
@@ -206,8 +165,6 @@
                              bool defer_read_completed);
 
  private:
-  // Whether the URL request should be intercepted as a stream.
-  bool stream_has_handler_;
   bool plugin_available_;
   bool plugin_stale_;
 
@@ -257,66 +214,6 @@
   return accept_header;
 }
 
-bool MimeSniffingResourceHandlerTest::TestStreamIsIntercepted(
-    ResourceInterceptPolicy resource_intercept_policy,
-    bool must_download,
-    ResourceType request_resource_type) {
-  net::URLRequestContext context;
-  std::unique_ptr<net::URLRequest> request(context.CreateRequest(
-      GURL("http://www.google.com"), net::DEFAULT_PRIORITY, nullptr,
-      TRAFFIC_ANNOTATION_FOR_TESTS));
-  bool is_main_frame = request_resource_type == ResourceType::kMainFrame;
-  ResourceRequestInfo::AllocateForTesting(request.get(), request_resource_type,
-                                          nullptr,        // context
-                                          0,              // render_process_id
-                                          0,              // render_view_id
-                                          0,              // render_frame_id
-                                          is_main_frame,  // is_main_frame
-                                          resource_intercept_policy,
-                                          true,          // is_async
-                                          PREVIEWS_OFF,  // previews_state
-                                          nullptr);      // navigation_ui_data
-
-  TestResourceDispatcherHost host(stream_has_handler_);
-  TestContentBrowserClient new_client(must_download);
-  ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client);
-
-  TestFakePluginService plugin_service(plugin_available_, plugin_stale_);
-
-  std::unique_ptr<InterceptingResourceHandler> intercepting_handler(
-      new InterceptingResourceHandler(std::make_unique<TestResourceHandler>(),
-                                      nullptr));
-  std::unique_ptr<TestResourceHandler> scoped_test_handler(
-      new TestResourceHandler());
-  scoped_test_handler->set_on_response_started_result(false);
-  MimeSniffingResourceHandler mime_sniffing_handler(
-      std::unique_ptr<ResourceHandler>(std::move(scoped_test_handler)), &host,
-      &plugin_service, intercepting_handler.get(), request.get(),
-      blink::mojom::RequestContextType::UNSPECIFIED);
-
-  MockResourceLoader mock_loader(&mime_sniffing_handler);
-
-  scoped_refptr<network::ResourceResponse> response(
-      new network::ResourceResponse);
-  // The MIME type isn't important but it shouldn't be empty.
-  response->head.mime_type = "application/pdf";
-
-  EXPECT_EQ(MockResourceLoader::Status::IDLE,
-            mock_loader.OnWillStart(request->url()));
-
-  mock_loader.OnResponseStarted(std::move(response));
-  mock_loader.WaitUntilIdleOrCanceled();
-  EXPECT_EQ(MockResourceLoader::Status::CANCELED, mock_loader.status());
-  EXPECT_EQ(net::ERR_ABORTED, mock_loader.error_code());
-
-  content::RunAllPendingInMessageLoop();
-  EXPECT_LT(host.intercepted_as_stream_count(), 2);
-  if (resource_intercept_policy != ResourceInterceptPolicy::kAllowNone)
-    EXPECT_TRUE(intercepting_handler->new_handler_for_testing());
-  SetBrowserClientForTesting(old_client);
-  return host.intercepted_as_stream();
-}
-
 void MimeSniffingResourceHandlerTest::TestHandlerSniffing(
     bool response_started,
     bool defer_response_started,
@@ -340,7 +237,7 @@
                                           PREVIEWS_OFF,  // previews_state
                                           nullptr);      // navigation_ui_data
 
-  TestResourceDispatcherHost host(false);
+  TestResourceDispatcherHost host;
 
   TestFakePluginService plugin_service(plugin_available_, plugin_stale_);
   std::unique_ptr<InterceptingResourceHandler> intercepting_handler(
@@ -504,7 +401,7 @@
                                           PREVIEWS_OFF,  // previews_state
                                           nullptr);      // navigation_ui_data
 
-  TestResourceDispatcherHost host(false);
+  TestResourceDispatcherHost host;
 
   TestFakePluginService plugin_service(plugin_available_, plugin_stale_);
   std::unique_ptr<InterceptingResourceHandler> intercepting_handler(
@@ -628,104 +525,6 @@
   content::RunAllPendingInMessageLoop();
 }
 
-// Test that stream requests are correctly intercepted under the right
-// circumstances. Test is not relevent when plugins are disabled.
-#if BUILDFLAG(ENABLE_PLUGINS)
-TEST_F(MimeSniffingResourceHandlerTest, StreamHandling) {
-  ResourceInterceptPolicy resource_intercept_policy;
-  bool must_download;
-  ResourceType resource_type;
-
-  // Ensure the stream is handled by MaybeInterceptAsStream in the
-  // ResourceDispatcherHost.
-  set_stream_has_handler(true);
-  set_plugin_available(true);
-
-  // Main frame request with no download allowed. Stream shouldn't be
-  // intercepted.
-  resource_intercept_policy = ResourceInterceptPolicy::kAllowNone;
-  must_download = false;
-  resource_type = ResourceType::kMainFrame;
-  EXPECT_FALSE(TestStreamIsIntercepted(resource_intercept_policy, must_download,
-                                       resource_type));
-
-  // Main frame request with no download allowed only after plugin handler is
-  // checked. Stream should be intercepted.
-  resource_intercept_policy = ResourceInterceptPolicy::kAllowPluginOnly;
-  must_download = false;
-  resource_type = ResourceType::kMainFrame;
-  EXPECT_TRUE(TestStreamIsIntercepted(resource_intercept_policy, must_download,
-                                      resource_type));
-
-  // Main frame request with download allowed. Stream should be intercepted.
-  resource_intercept_policy = ResourceInterceptPolicy::kAllowAll;
-  must_download = false;
-  resource_type = ResourceType::kMainFrame;
-  EXPECT_TRUE(TestStreamIsIntercepted(resource_intercept_policy, must_download,
-                                      resource_type));
-
-  // Main frame request with download forced. Stream shouldn't be intercepted.
-  resource_intercept_policy = ResourceInterceptPolicy::kAllowAll;
-  must_download = true;
-  resource_type = ResourceType::kMainFrame;
-  EXPECT_FALSE(TestStreamIsIntercepted(resource_intercept_policy, must_download,
-                                       resource_type));
-
-  // Sub-resource request with download not allowed. Stream shouldn't be
-  // intercepted.
-  resource_intercept_policy = ResourceInterceptPolicy::kAllowNone;
-  must_download = false;
-  resource_type = ResourceType::kSubResource;
-  EXPECT_FALSE(TestStreamIsIntercepted(resource_intercept_policy, must_download,
-                                       resource_type));
-
-  // Plugin resource request with download not allowed. Stream shouldn't be
-  // intercepted.
-  resource_intercept_policy = ResourceInterceptPolicy::kAllowNone;
-  must_download = false;
-  resource_type = ResourceType::kPluginResource;
-  EXPECT_FALSE(TestStreamIsIntercepted(resource_intercept_policy, must_download,
-                                       resource_type));
-
-  // Object request with download not allowed. Stream should be intercepted.
-  resource_intercept_policy = ResourceInterceptPolicy::kAllowNone;
-  must_download = false;
-  resource_type = ResourceType::kObject;
-  EXPECT_TRUE(TestStreamIsIntercepted(resource_intercept_policy, must_download,
-                                      resource_type));
-
-  // Test the cases where the stream isn't handled by MaybeInterceptAsStream
-  // in the ResourceDispatcherHost.
-  set_stream_has_handler(false);
-  resource_intercept_policy = ResourceInterceptPolicy::kAllowNone;
-  must_download = false;
-  resource_type = ResourceType::kObject;
-  EXPECT_FALSE(TestStreamIsIntercepted(resource_intercept_policy, must_download,
-                                       resource_type));
-
-  // Test the cases where the stream handled by MaybeInterceptAsStream
-  // with plugin not available. This is the case when intercepting streams for
-  // the streamsPrivate extensions API.
-  set_stream_has_handler(true);
-  set_plugin_available(false);
-  resource_intercept_policy = ResourceInterceptPolicy::kAllowNone;
-  must_download = false;
-  resource_type = ResourceType::kObject;
-  EXPECT_TRUE(TestStreamIsIntercepted(resource_intercept_policy, must_download,
-                                      resource_type));
-
-  // Test the cases where the stream handled by MaybeInterceptAsStream
-  // with plugin not available. This is the case when intercepting streams for
-  // the streamsPrivate extensions API with stale plugin.
-  set_plugin_stale(true);
-  resource_intercept_policy = ResourceInterceptPolicy::kAllowNone;
-  must_download = false;
-  resource_type = ResourceType::kObject;
-  EXPECT_TRUE(TestStreamIsIntercepted(resource_intercept_policy, must_download,
-                                      resource_type));
-}
-#endif
-
 // Test that the MimeSniffingHandler operates properly when it doesn't sniff
 // resources.
 // TODO(mmenke):  None of these test async cancellation.  Should they?
@@ -854,7 +653,7 @@
                                           PREVIEWS_OFF,  // previews_state
                                           nullptr);      // navigation_ui_data
 
-  TestResourceDispatcherHost host(false);
+  TestResourceDispatcherHost host;
 
   TestFakePluginService plugin_service(false, false);
   std::unique_ptr<ResourceHandler> intercepting_handler(
@@ -905,7 +704,7 @@
                                           PREVIEWS_OFF,  // previews_state
                                           nullptr);      // navigation_ui_data
 
-  TestResourceDispatcherHost host(false);
+  TestResourceDispatcherHost host;
 
   TestFakePluginService plugin_service(false, false);
   std::unique_ptr<InterceptingResourceHandler> intercepting_handler(
@@ -965,7 +764,7 @@
                                           PREVIEWS_OFF,  // previews_state
                                           nullptr);      // navigation_ui_data
 
-  TestResourceDispatcherHost host(false);
+  TestResourceDispatcherHost host;
 
   TestFakePluginService plugin_service(false, false);
   std::unique_ptr<InterceptingResourceHandler> intercepting_handler(
@@ -1043,7 +842,7 @@
                                           PREVIEWS_OFF,  // previews_state
                                           nullptr);      // navigation_ui_data
 
-  TestResourceDispatcherHost host(false);
+  TestResourceDispatcherHost host;
 
   TestFakePluginService plugin_service(false, false);
   std::unique_ptr<InterceptingResourceHandler> intercepting_handler(
diff --git a/content/browser/loader/mojo_async_resource_handler_unittest.cc b/content/browser/loader/mojo_async_resource_handler_unittest.cc
index 3df9e05..a6acea3 100644
--- a/content/browser/loader/mojo_async_resource_handler_unittest.cc
+++ b/content/browser/loader/mojo_async_resource_handler_unittest.cc
@@ -29,7 +29,6 @@
 #include "content/public/browser/resource_context.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/browser/resource_throttle.h"
-#include "content/public/browser/stream_info.h"
 #include "content/public/common/previews_state.h"
 #include "content/public/common/resource_type.h"
 #include "content/public/test/test_browser_context.h"
@@ -129,19 +128,6 @@
     ADD_FAILURE() << "DownloadStarting should not be called.";
   }
 
-  bool ShouldInterceptResourceAsStream(net::URLRequest* request,
-                                       const std::string& mime_type,
-                                       GURL* origin,
-                                       std::string* payload) override {
-    ADD_FAILURE() << "ShouldInterceptResourceAsStream should not be called.";
-    return false;
-  }
-
-  void OnStreamCreated(net::URLRequest* request,
-                       std::unique_ptr<content::StreamInfo> stream) override {
-    ADD_FAILURE() << "OnStreamCreated should not be called.";
-  }
-
   void OnResponseStarted(net::URLRequest* request,
                          ResourceContext* resource_context,
                          network::ResourceResponse* response) override {}
diff --git a/content/browser/loader/navigation_url_loader_delegate.h b/content/browser/loader/navigation_url_loader_delegate.h
index 5008698..270ab931 100644
--- a/content/browser/loader/navigation_url_loader_delegate.h
+++ b/content/browser/loader/navigation_url_loader_delegate.h
@@ -58,7 +58,6 @@
       const GlobalRequestID& request_id,
       bool is_download,
       NavigationDownloadPolicy download_policy,
-      bool is_stream,
       base::Optional<SubresourceLoaderParams> subresource_loader_params) = 0;
 
   // Called if the request fails before receving a response. Specific
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index b903512..b938ff82 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -1067,7 +1067,6 @@
     }
 
     bool is_download;
-    bool is_stream;
 
     std::unique_ptr<NavigationData> cloned_navigation_data;
 
@@ -1090,7 +1089,6 @@
     // When a plugin intercepted the response, we don't want to download it.
     is_download =
         !head.intercepted_by_plugin && (must_download || !known_mime_type);
-    is_stream = false;
 
     // If NetworkService is on, or an interceptor handled the request, the
     // request doesn't use ResourceDispatcherHost so
@@ -1098,8 +1096,7 @@
     if (base::FeatureList::IsEnabled(network::features::kNetworkService) ||
         !default_loader_used_) {
       CallOnReceivedResponse(head, std::move(url_loader_client_endpoints),
-                             std::move(cloned_navigation_data), is_download,
-                             is_stream);
+                             std::move(cloned_navigation_data), is_download);
       return;
     }
 
@@ -1114,7 +1111,6 @@
       ResourceRequestInfoImpl* info =
           ResourceRequestInfoImpl::ForRequest(url_request);
       is_download = !head.intercepted_by_plugin && info->IsDownload();
-      is_stream = info->is_stream();
       if (rdh->delegate()) {
         NavigationData* navigation_data =
             rdh->delegate()->GetNavigationData(url_request);
@@ -1125,12 +1121,11 @@
           cloned_navigation_data = navigation_data->Clone();
       }
     } else {
-      is_download = is_stream = false;
+      is_download = false;
     }
 
     CallOnReceivedResponse(head, std::move(url_loader_client_endpoints),
-                           std::move(cloned_navigation_data), is_download,
-                           is_stream);
+                           std::move(cloned_navigation_data), is_download);
   }
 
 #if BUILDFLAG(ENABLE_PLUGINS)
@@ -1164,7 +1159,7 @@
     bool is_download = !has_plugin && is_download_if_not_handled_by_plugin;
 
     CallOnReceivedResponse(head, std::move(url_loader_client_endpoints),
-                           nullptr, is_download, false /* is_stream */);
+                           nullptr, is_download);
   }
 #endif
 
@@ -1172,8 +1167,7 @@
       const network::ResourceResponseHead& head,
       network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
       std::unique_ptr<NavigationData> cloned_navigation_data,
-      bool is_download,
-      bool is_stream) {
+      bool is_download) {
     scoped_refptr<network::ResourceResponse> response(
         new network::ResourceResponse());
     response->head = head;
@@ -1186,11 +1180,11 @@
     // response. https://crbug.com/416050
     base::PostTaskWithTraits(
         FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(
-            &NavigationURLLoaderImpl::OnReceiveResponse, owner_,
-            response->DeepCopy(), std::move(url_loader_client_endpoints),
-            std::move(cloned_navigation_data), global_request_id_, is_download,
-            is_stream, ui_to_io_time_, base::Time::Now()));
+        base::BindOnce(&NavigationURLLoaderImpl::OnReceiveResponse, owner_,
+                       response->DeepCopy(),
+                       std::move(url_loader_client_endpoints),
+                       std::move(cloned_navigation_data), global_request_id_,
+                       is_download, ui_to_io_time_, base::Time::Now()));
   }
 
   void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
@@ -1688,7 +1682,6 @@
     std::unique_ptr<NavigationData> navigation_data,
     const GlobalRequestID& global_request_id,
     bool is_download,
-    bool is_stream,
     base::TimeDelta total_ui_to_io_time,
     base::Time io_post_time) {
   const base::TimeDelta kMinTime = base::TimeDelta::FromMicroseconds(1);
@@ -1710,8 +1703,7 @@
   delegate_->OnResponseStarted(
       std::move(response), std::move(url_loader_client_endpoints),
       std::move(navigation_data), global_request_id, is_download,
-      download_policy_, is_stream,
-      request_controller_->TakeSubresourceLoaderParams());
+      download_policy_, request_controller_->TakeSubresourceLoaderParams());
 }
 
 void NavigationURLLoaderImpl::OnReceiveRedirect(
diff --git a/content/browser/loader/navigation_url_loader_impl.h b/content/browser/loader/navigation_url_loader_impl.h
index 5bbcb18..2d77856 100644
--- a/content/browser/loader/navigation_url_loader_impl.h
+++ b/content/browser/loader/navigation_url_loader_impl.h
@@ -61,7 +61,6 @@
       std::unique_ptr<NavigationData> navigation_data,
       const GlobalRequestID& global_request_id,
       bool is_download,
-      bool is_stream,
       base::TimeDelta total_ui_to_io_time,
       base::Time io_post_time);
   void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index 934dee8..89e10f6 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -53,13 +53,9 @@
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/browser/loader/resource_requester_info.h"
 #include "content/browser/loader/sec_fetch_site_resource_handler.h"
-#include "content/browser/loader/stream_resource_handler.h"
 #include "content/browser/loader/throttling_resource_handler.h"
 #include "content/browser/loader/upload_data_stream_builder.h"
 #include "content/browser/resource_context_impl.h"
-#include "content/browser/streams/stream.h"
-#include "content/browser/streams/stream_context.h"
-#include "content/browser/streams/stream_registry.h"
 #include "content/browser/web_package/signed_exchange_consts.h"
 #include "content/browser/web_package/signed_exchange_utils.h"
 #include "content/common/net/url_request_service_worker_data.h"
@@ -75,7 +71,6 @@
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/browser/resource_throttle.h"
 #include "content/public/browser/site_isolation_policy.h"
-#include "content/public/browser/stream_info.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_constants.h"
@@ -473,14 +468,13 @@
 #ifndef NDEBUG
   for (const auto& loader : loaders_to_cancel) {
     // There is no strict requirement that this be the case, but currently
-    // downloads, streams, detachable requests, transferred requests, and
+    // downloads, detachable requests, transferred requests, and
     // browser-owned requests are the only requests that aren't cancelled when
     // the associated processes go away. It may be OK for this invariant to
     // change in the future, but if this assertion fires without the invariant
     // changing, then it's indicative of a leak.
     DCHECK(
         loader->GetRequestInfo()->IsDownload() ||
-        loader->GetRequestInfo()->is_stream() ||
         (loader->GetRequestInfo()->detachable_handler() &&
          loader->GetRequestInfo()->detachable_handler()->is_detached()) ||
         loader->GetRequestInfo()->requester_info()->IsBrowserSideNavigation() ||
@@ -540,44 +534,6 @@
   return handler;
 }
 
-std::unique_ptr<ResourceHandler>
-ResourceDispatcherHostImpl::MaybeInterceptAsStream(
-    net::URLRequest* request,
-    network::ResourceResponse* response,
-    std::string* payload) {
-  payload->clear();
-  const std::string& mime_type = response->head.mime_type;
-
-  GURL origin;
-  if (!delegate_ || !delegate_->ShouldInterceptResourceAsStream(
-                        request, mime_type, &origin, payload)) {
-    return nullptr;
-  }
-
-  ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request);
-  StreamContext* stream_context =
-      GetStreamContextForResourceContext(info->GetContext());
-
-  auto handler = std::make_unique<StreamResourceHandler>(
-      request, stream_context->registry(), origin, false);
-
-  info->set_is_stream(true);
-  auto stream_info = std::make_unique<StreamInfo>();
-  stream_info->handle = handler->stream()->CreateHandle();
-  stream_info->original_url = request->url();
-  stream_info->mime_type = mime_type;
-  // Make a copy of the response headers so it is safe to pass across threads;
-  // the old handler (AsyncResourceHandler) may modify it in parallel via the
-  // ResourceDispatcherHostDelegate.
-  if (response->head.headers.get()) {
-    stream_info->response_headers =
-        base::MakeRefCounted<net::HttpResponseHeaders>(
-            response->head.headers->raw_headers());
-  }
-  delegate_->OnStreamCreated(request, std::move(stream_info));
-  return std::move(handler);
-}
-
 std::unique_ptr<LoginDelegate> ResourceDispatcherHostImpl::CreateLoginDelegate(
     ResourceLoader* loader,
     const net::AuthChallengeInfo& auth_info) {
@@ -1014,7 +970,6 @@
       static_cast<ResourceType>(request_data.resource_type),
       static_cast<ui::PageTransition>(request_data.transition_type),
       false,  // is download
-      false,  // is stream
       ResourceInterceptPolicy::kAllowNone, request_data.has_user_gesture,
       request_data.enable_load_timing, request_data.enable_upload_progress,
       request_data.do_not_prompt_for_login, request_data.keepalive,
@@ -1202,7 +1157,6 @@
       {},     // fetch_window_id
       ResourceType::kSubResource, ui::PAGE_TRANSITION_LINK,
       download,  // is_download
-      false,     // is_stream
       download ? ResourceInterceptPolicy::kAllowAll
                : ResourceInterceptPolicy::kAllowNone,
       false,  // has_user_gesture
@@ -1283,7 +1237,7 @@
         // deliberately, so we don't cancel it here.
       } else if (info->detachable_handler()) {
         info->detachable_handler()->Detach();
-      } else if (!info->IsDownload() && !info->is_stream()) {
+      } else if (!info->IsDownload()) {
         matching_requests.push_back(id);
       }
     }
@@ -1542,7 +1496,6 @@
       info.is_main_frame, {},  // fetch_window_id
       resource_type, info.common_params.transition,
       false,  // is download
-      false,  // is stream
       info.common_params.download_policy.GetResourceInterceptPolicy(),
       info.common_params.has_user_gesture,
       true,   // enable_load_timing
diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h
index fe0937e..fc20c92 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.h
+++ b/content/browser/loader/resource_dispatcher_host_impl.h
@@ -35,7 +35,6 @@
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/browser/resource_request_info.h"
-#include "content/public/browser/stream_handle.h"
 #include "content/public/common/previews_state.h"
 #include "content/public/common/resource_type.h"
 #include "net/base/load_states.h"
@@ -189,22 +188,6 @@
       bool must_download,
       bool is_new_request);
 
-  // Called to determine whether the response to |request| should be intercepted
-  // and handled as a stream. Streams are used to pass direct access to a
-  // resource response to another application (e.g. a web page) without being
-  // handled by the browser itself. If the request should be intercepted as a
-  // stream, a StreamResourceHandler is returned which provides access to the
-  // response.
-  //
-  // This function must be called after the ResourceRequestInfo has been created
-  // and associated with the request. If |payload| is set to a non-empty value,
-  // the caller must send it to the old resource handler instead of cancelling
-  // it.
-  virtual std::unique_ptr<ResourceHandler> MaybeInterceptAsStream(
-      net::URLRequest* request,
-      network::ResourceResponse* response,
-      std::string* payload);
-
   network::ResourceScheduler* scheduler() { return scheduler_.get(); }
 
   // Called by a ResourceHandler when it's ready to start reading data and
diff --git a/content/browser/loader/resource_loader.cc b/content/browser/loader/resource_loader.cc
index 6bd5b8d..dbec9c9 100644
--- a/content/browser/loader/resource_loader.cc
+++ b/content/browser/loader/resource_loader.cc
@@ -643,7 +643,7 @@
   // WebKit will send us a cancel for downloads since it no longer handles
   // them.  In this case, ignore the cancel since we handle downloads in the
   // browser.
-  if (from_renderer && (info->IsDownload() || info->is_stream()))
+  if (from_renderer && info->IsDownload())
     return;
 
   if (from_renderer && info->detachable_handler()) {
diff --git a/content/browser/loader/resource_request_info_impl.cc b/content/browser/loader/resource_request_info_impl.cc
index 39fbfc14..dfa2243 100644
--- a/content/browser/loader/resource_request_info_impl.cc
+++ b/content/browser/loader/resource_request_info_impl.cc
@@ -74,7 +74,6 @@
       resource_type,                             // resource_type
       ui::PAGE_TRANSITION_LINK,                  // transition_type
       false,                                     // is_download
-      false,                                     // is_stream
       resource_intercept_policy,                 // resource_intercept_policy
       false,                                     // has_user_gesture
       false,                                     // enable load timing
@@ -137,7 +136,6 @@
     ResourceType resource_type,
     ui::PageTransition transition_type,
     bool is_download,
-    bool is_stream,
     ResourceInterceptPolicy resource_intercept_policy,
     bool has_user_gesture,
     bool enable_load_timing,
@@ -163,7 +161,6 @@
       is_main_frame_(is_main_frame),
       fetch_window_id_(fetch_window_id),
       is_download_(is_download),
-      is_stream_(is_stream),
       resource_intercept_policy_(resource_intercept_policy),
       has_user_gesture_(has_user_gesture),
       enable_load_timing_(enable_load_timing),
diff --git a/content/browser/loader/resource_request_info_impl.h b/content/browser/loader/resource_request_info_impl.h
index 7b87bf4f..cbdd3c2 100644
--- a/content/browser/loader/resource_request_info_impl.h
+++ b/content/browser/loader/resource_request_info_impl.h
@@ -53,7 +53,6 @@
       ResourceType resource_type,
       ui::PageTransition transition_type,
       bool is_download,
-      bool is_stream,
       ResourceInterceptPolicy resource_intercept_policy,
       bool has_user_gesture,
       bool enable_load_timing,
@@ -138,10 +137,6 @@
   // Whether this is a download.
   void set_is_download(bool download) { is_download_ = download; }
 
-  // Whether this is a stream.
-  bool is_stream() const { return is_stream_; }
-  void set_is_stream(bool stream) { is_stream_ = stream; }
-
   // Whether this request has been counted towards the number of in flight
   // requests, which is only true for requests that require a file descriptor
   // for their shared memory buffer.
@@ -229,7 +224,6 @@
   bool is_main_frame_;
   base::UnguessableToken fetch_window_id_;
   bool is_download_;
-  bool is_stream_;
   ResourceInterceptPolicy resource_intercept_policy_;
   bool has_user_gesture_;
   bool enable_load_timing_;
diff --git a/content/browser/loader/stream_resource_handler.cc b/content/browser/loader/stream_resource_handler.cc
deleted file mode 100644
index 9a48790..0000000
--- a/content/browser/loader/stream_resource_handler.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/loader/stream_resource_handler.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "content/browser/loader/resource_controller.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_status.h"
-
-namespace content {
-
-StreamResourceHandler::StreamResourceHandler(net::URLRequest* request,
-                                             StreamRegistry* registry,
-                                             const GURL& origin,
-                                             bool immediate_mode)
-    : ResourceHandler(request) {
-  writer_.InitializeStream(registry, origin,
-                           base::Bind(&StreamResourceHandler::OutOfBandCancel,
-                                      base::Unretained(this), net::ERR_ABORTED,
-                                      true /* tell_renderer */));
-  writer_.set_immediate_mode(immediate_mode);
-}
-
-StreamResourceHandler::~StreamResourceHandler() {
-}
-
-void StreamResourceHandler::OnRequestRedirected(
-    const net::RedirectInfo& redirect_info,
-    network::ResourceResponse* resp,
-    std::unique_ptr<ResourceController> controller) {
-  controller->Resume();
-}
-
-void StreamResourceHandler::OnResponseStarted(
-    network::ResourceResponse* resp,
-    std::unique_ptr<ResourceController> controller) {
-  writer_.OnResponseStarted(request()->response_info());
-  controller->Resume();
-}
-
-void StreamResourceHandler::OnWillStart(
-    const GURL& url,
-    std::unique_ptr<ResourceController> controller) {
-  controller->Resume();
-}
-
-void StreamResourceHandler::OnWillRead(
-    scoped_refptr<net::IOBuffer>* buf,
-    int* buf_size,
-    std::unique_ptr<ResourceController> controller) {
-  writer_.OnWillRead(buf, buf_size, -1);
-  controller->Resume();
-}
-
-void StreamResourceHandler::OnReadCompleted(
-    int bytes_read,
-    std::unique_ptr<ResourceController> controller) {
-  int64_t total_bytes_read = request()->GetTotalReceivedBytes();
-  int64_t raw_body_bytes = request()->GetRawBodyBytes();
-  writer_.UpdateNetworkStats(raw_body_bytes, total_bytes_read);
-  writer_.OnReadCompleted(bytes_read,
-                          base::Bind(&ResourceController::Resume,
-                                     base::Passed(std::move(controller))));
-}
-
-void StreamResourceHandler::OnResponseCompleted(
-    const net::URLRequestStatus& status,
-    std::unique_ptr<ResourceController> controller) {
-  writer_.Finalize(status.error());
-  controller->Resume();
-}
-
-}  // namespace content
diff --git a/content/browser/loader/stream_resource_handler.h b/content/browser/loader/stream_resource_handler.h
deleted file mode 100644
index b6f51bba..0000000
--- a/content/browser/loader/stream_resource_handler.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_LOADER_STREAM_RESOURCE_HANDLER_H_
-#define CONTENT_BROWSER_LOADER_STREAM_RESOURCE_HANDLER_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "content/browser/loader/resource_handler.h"
-#include "content/browser/loader/stream_writer.h"
-
-namespace net {
-class URLRequest;
-}  // namespace net
-
-namespace content {
-
-class ResourceController;
-class StreamRegistry;
-
-// Redirect this resource to a stream.
-class StreamResourceHandler : public ResourceHandler {
- public:
-  // |origin| will be used to construct the URL for the Stream. See
-  // WebCore::BlobURL and and WebCore::SecurityOrigin in Blink to understand
-  // how origin check is done on resource loading.
-  StreamResourceHandler(net::URLRequest* request,
-                        StreamRegistry* registry,
-                        const GURL& origin,
-                        bool immediate_mode);
-  ~StreamResourceHandler() override;
-
-  // Not needed, as this event handler ought to be the final resource.
-  void OnRequestRedirected(
-      const net::RedirectInfo& redirect_info,
-      network::ResourceResponse* resp,
-      std::unique_ptr<ResourceController> controller) override;
-
-  void OnResponseStarted(
-      network::ResourceResponse* resp,
-      std::unique_ptr<ResourceController> controller) override;
-
-  void OnWillStart(const GURL& url,
-                   std::unique_ptr<ResourceController> controller) override;
-
-  // Create a new buffer to store received data.
-  void OnWillRead(scoped_refptr<net::IOBuffer>* buf,
-                  int* buf_size,
-                  std::unique_ptr<ResourceController> controller) override;
-
-  // A read was completed, forward the data to the Stream.
-  void OnReadCompleted(int bytes_read,
-                       std::unique_ptr<ResourceController> controller) override;
-
-  void OnResponseCompleted(
-      const net::URLRequestStatus& status,
-      std::unique_ptr<ResourceController> controller) override;
-
-  Stream* stream() { return writer_.stream(); }
-
- private:
-  StreamWriter writer_;
-
-  DISALLOW_COPY_AND_ASSIGN(StreamResourceHandler);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_LOADER_STREAM_RESOURCE_HANDLER_H_
diff --git a/content/browser/loader/stream_writer.cc b/content/browser/loader/stream_writer.cc
deleted file mode 100644
index 09db049..0000000
--- a/content/browser/loader/stream_writer.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/loader/stream_writer.h"
-
-#include <utility>
-
-#include "base/guid.h"
-#include "content/browser/loader/resource_controller.h"
-#include "content/browser/streams/stream.h"
-#include "content/browser/streams/stream_registry.h"
-#include "net/base/io_buffer.h"
-#include "url/gurl.h"
-#include "url/url_constants.h"
-
-namespace content {
-
-StreamWriter::StreamWriter() : immediate_mode_(false) {}
-
-StreamWriter::~StreamWriter() {
-  if (stream_.get())
-    Finalize(0);
-}
-
-void StreamWriter::InitializeStream(StreamRegistry* registry,
-                                    const GURL& origin,
-                                    const base::Closure& cancel_callback) {
-  cancel_callback_ = cancel_callback;
-  DCHECK(!stream_.get());
-
-  // TODO(tyoshino): Find a way to share this with the blob URL creation in
-  // WebKit.
-  GURL url(std::string(url::kBlobScheme) + ":" + origin.spec() +
-           base::GenerateGUID());
-  stream_ = new Stream(registry, this, url);
-}
-
-void StreamWriter::OnResponseStarted(
-    const net::HttpResponseInfo& response_info) {
-  stream_->OnResponseStarted(response_info);
-}
-
-void StreamWriter::UpdateNetworkStats(int64_t raw_body_bytes,
-                                      int64_t total_bytes) {
-  stream_->UpdateNetworkStats(raw_body_bytes, total_bytes);
-}
-
-void StreamWriter::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
-                              int* buf_size,
-                              int min_size) {
-  static const int kReadBufSize = 32768;
-
-  DCHECK(buf);
-  DCHECK(buf_size);
-  DCHECK_LE(min_size, kReadBufSize);
-  if (!read_buffer_.get())
-    read_buffer_ = base::MakeRefCounted<net::IOBuffer>(kReadBufSize);
-  *buf = read_buffer_.get();
-  *buf_size = kReadBufSize;
-}
-
-void StreamWriter::OnReadCompleted(
-    int bytes_read,
-    const base::Closure& need_more_data_callback) {
-  DCHECK(!need_more_data_callback_);
-  if (!bytes_read) {
-    need_more_data_callback.Run();
-    return;
-  }
-
-  // We have more data to read.
-  DCHECK(read_buffer_.get());
-
-  // Release the ownership of the buffer, and store a reference
-  // to it. A new one will be allocated in OnWillRead().
-  scoped_refptr<net::IOBuffer> buffer;
-  read_buffer_.swap(buffer);
-  stream_->AddData(buffer, bytes_read);
-
-  if (immediate_mode_)
-    stream_->Flush();
-
-  if (!stream_->can_add_data()) {
-    need_more_data_callback_ = need_more_data_callback;
-    return;
-  }
-  need_more_data_callback.Run();
-}
-
-void StreamWriter::Finalize(int status) {
-  DCHECK(stream_.get());
-  stream_->Finalize(status);
-  stream_->RemoveWriteObserver(this);
-  stream_ = nullptr;
-}
-
-void StreamWriter::OnSpaceAvailable(Stream* stream) {
-  std::move(need_more_data_callback_).Run();
-}
-
-void StreamWriter::OnClose(Stream* stream) {
-  cancel_callback_.Run();
-}
-
-}  // namespace content
diff --git a/content/browser/loader/stream_writer.h b/content/browser/loader/stream_writer.h
deleted file mode 100644
index 09188b8..0000000
--- a/content/browser/loader/stream_writer.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_LOADER_STREAM_WRITER_H_
-#define CONTENT_BROWSER_LOADER_STREAM_WRITER_H_
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "content/browser/streams/stream_write_observer.h"
-
-class GURL;
-
-namespace net {
-class IOBuffer;
-class HttpResponseInfo;
-}
-
-namespace content {
-
-class Stream;
-class StreamRegistry;
-
-// StreamWriter is a helper class for ResourceHandlers which route their output
-// into a Stream. It manages an internal buffer and handles back-pressure from
-// the Stream's reader.
-class StreamWriter : public StreamWriteObserver {
- public:
-  // Creates a new StreamWriter without an initialized Stream or controller. The
-  // controller must be set before the writer is used.
-  StreamWriter();
-  ~StreamWriter() override;
-
-  Stream* stream() { return stream_.get(); }
-
-  // When immediate mode is enabled, the |stream_| is flushed every time new
-  // data is made available by calls to OnReadCompleted.
-  void set_immediate_mode(bool enabled) { immediate_mode_ = enabled; }
-
-  // Initializes the writer with a new Stream in |registry|. |origin| will be
-  // used to construct the URL for the Stream. See WebCore::BlobURL and and
-  // WebCore::SecurityOrigin in Blink to understand how origin check is done on
-  // resource loading. |cancel_callback| must be called if the StreamWriter
-  // closes the stream.
-  void InitializeStream(StreamRegistry* registry,
-                        const GURL& origin,
-                        const base::Closure& cancel_callback);
-
-  // Passes HTTP response information associated with the response body
-  // transferred through this stream. This should be called before
-  // OnReadCompleted is ever called.
-  void OnResponseStarted(const net::HttpResponseInfo& response_info);
-
-  // Updates actual counts of bytes transferred by the network.
-  void UpdateNetworkStats(int64_t raw_body_bytes, int64_t total_bytes);
-
-  // Prepares a buffer to read data from the request. This call will be followed
-  // by either OnReadCompleted (on successful read or EOF) or destruction.  The
-  // buffer may not be recycled until OnReadCompleted is called. If |min_size|
-  // is not -1, it is the minimum size of the returned buffer.
-  //
-  // OnWillRead may be called before the stream is initialized. This is to
-  // support MimeTypeResourceHandler which reads the initial chunk of data
-  // early.
-  void OnWillRead(scoped_refptr<net::IOBuffer>* buf,
-                  int* buf_size,
-                  int min_size);
-
-  // A read was completed, forward the data to the Stream.
-  // |need_more_data_callback| must be called (synchronously or asynchronously)
-  // once the writer is ready for more data.  Invoking the callback may result
-  // in more data being received recursively.
-  //
-  // InitializeStream must have been called before calling OnReadCompleted.
-  void OnReadCompleted(int bytes_read,
-                       const base::Closure& need_more_data_callback);
-
-  // Called when there is no more data to read to the stream.
-  void Finalize(int status);
-
- private:
-  // StreamWriteObserver implementation.
-  void OnSpaceAvailable(Stream* stream) override;
-  void OnClose(Stream* stream) override;
-
-  scoped_refptr<Stream> stream_;
-  scoped_refptr<net::IOBuffer> read_buffer_;
-  bool immediate_mode_;
-
-  base::Closure cancel_callback_;
-  base::Closure need_more_data_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(StreamWriter);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_LOADER_STREAM_WRITER_H_
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
index e02e585c..7137392 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
@@ -228,16 +228,6 @@
   factory3_.Reset();
 }
 
-bool DWriteFontLookupTableBuilder::EnsureFontUniqueNameTableForTesting() {
-  TRACE_EVENT0("dwrite,fonts",
-               "DWriteFontLookupTableBuilder::EnsureFontUniqueNameTable");
-  DCHECK(base::FeatureList::IsEnabled(features::kFontSrcLocalMatching));
-  DCHECK(!HasDWriteUniqueFontLookups());
-  base::ScopedAllowBaseSyncPrimitives allow_base_sync_primitives;
-  font_table_built_.Wait();
-  return IsFontUniqueNameTableValid();
-}
-
 base::TimeDelta DWriteFontLookupTableBuilder::IndexingTimeout() {
   return font_indexing_timeout_;
 }
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
index 0543a28..2c0c17d 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
@@ -106,8 +106,6 @@
   // Windows 10, used for testing only to allow running the tests on Windows 10.
   void OverrideDWriteVersionChecksForTesting();
 
-  bool EnsureFontUniqueNameTableForTesting();
-
  private:
   friend class base::NoDestructor<DWriteFontLookupTableBuilder>;
 
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc
index 7cc8d19b..3a176aa 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/path_service.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "content/public/common/content_features.h"
@@ -36,9 +37,7 @@
 
 class DWriteFontLookupTableBuilderTest : public testing::Test {
  public:
-  DWriteFontLookupTableBuilderTest()
-      : scoped_task_environment_(
-            base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::ASYNC) {
+  DWriteFontLookupTableBuilderTest() {
     feature_list_.InitAndEnableFeature(features::kFontSrcLocalMatching);
   }
 
@@ -52,9 +51,6 @@
         scoped_temp_dir_.GetPath());
   }
 
- protected:
-  DWriteFontLookupTableBuilder* font_lookup_table_builder_;
-
   void TestMatchFonts() {
     base::ReadOnlySharedMemoryRegion font_table_memory =
         font_lookup_table_builder_->DuplicateMemoryRegion();
@@ -73,11 +69,12 @@
       ASSERT_EQ(test_font_name_index.ttc_index, match_result->ttc_index);
     }
   }
-  base::ScopedTempDir scoped_temp_dir_;
 
- private:
+ protected:
   base::test::ScopedFeatureList feature_list_;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  DWriteFontLookupTableBuilder* font_lookup_table_builder_;
+  base::ScopedTempDir scoped_temp_dir_;
 };
 
 class DWriteFontLookupTableBuilderTimeoutTest
@@ -92,26 +89,42 @@
 // class directly.
 TEST_F(DWriteFontLookupTableBuilderTest, TestFindUniqueFontDirect) {
   font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
-  font_lookup_table_builder_->EnsureFontUniqueNameTableForTesting();
-  TestMatchFonts();
+  bool test_callback_executed = false;
+  font_lookup_table_builder_->QueueShareMemoryRegionWhenReady(
+      base::SequencedTaskRunnerHandle::Get(),
+      base::BindLambdaForTesting(
+          [this, &test_callback_executed](base::ReadOnlySharedMemoryRegion) {
+            TestMatchFonts();
+            test_callback_executed = true;
+          }));
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(test_callback_executed);
 }
 
 TEST_P(DWriteFontLookupTableBuilderTimeoutTest, TestTimeout) {
   font_lookup_table_builder_->SetSlowDownIndexingForTestingWithTimeout(
       GetParam(), kTestingTimeout);
   font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
-  font_lookup_table_builder_->EnsureFontUniqueNameTableForTesting();
-  base::ReadOnlySharedMemoryRegion font_table_memory =
-      font_lookup_table_builder_->DuplicateMemoryRegion();
-  blink::FontTableMatcher font_table_matcher(font_table_memory.Map());
+  bool test_callback_executed = false;
+  font_lookup_table_builder_->QueueShareMemoryRegionWhenReady(
+      base::SequencedTaskRunnerHandle::Get(),
+      base::BindLambdaForTesting([this, &test_callback_executed](
+                                     base::ReadOnlySharedMemoryRegion
+                                         font_table_memory) {
+        blink::FontTableMatcher font_table_matcher(font_table_memory.Map());
 
-  for (auto& test_font_name_index : kExpectedTestFonts) {
-    base::Optional<blink::FontTableMatcher::MatchResult> match_result =
-        font_table_matcher.MatchName(test_font_name_index.font_name);
-    ASSERT_TRUE(!match_result);
-  }
-  if (GetParam() == DWriteFontLookupTableBuilder::SlowDownMode::kHangOneTask)
-    font_lookup_table_builder_->ResumeFromHangForTesting();
+        for (auto& test_font_name_index : kExpectedTestFonts) {
+          base::Optional<blink::FontTableMatcher::MatchResult> match_result =
+              font_table_matcher.MatchName(test_font_name_index.font_name);
+          ASSERT_TRUE(!match_result);
+        }
+        if (GetParam() ==
+            DWriteFontLookupTableBuilder::SlowDownMode::kHangOneTask)
+          font_lookup_table_builder_->ResumeFromHangForTesting();
+        test_callback_executed = true;
+      }));
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(test_callback_executed);
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -125,11 +138,20 @@
   font_lookup_table_builder_->SetSlowDownIndexingForTestingWithTimeout(
       DWriteFontLookupTableBuilder::SlowDownMode::kHangOneTask,
       kTestingTimeout);
+
   font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
+  bool test_callback_executed = false;
+  font_lookup_table_builder_->QueueShareMemoryRegionWhenReady(
+      base::SequencedTaskRunnerHandle::Get(),
+      base::BindLambdaForTesting(
+          [this, &test_callback_executed](base::ReadOnlySharedMemoryRegion) {
+            ASSERT_TRUE(font_lookup_table_builder_->FontUniqueNameTableReady());
+            test_callback_executed = true;
+          }));
   ASSERT_FALSE(font_lookup_table_builder_->FontUniqueNameTableReady());
   font_lookup_table_builder_->ResumeFromHangForTesting();
-  font_lookup_table_builder_->EnsureFontUniqueNameTableForTesting();
-  ASSERT_TRUE(font_lookup_table_builder_->FontUniqueNameTableReady());
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(test_callback_executed);
 }
 
 TEST_F(DWriteFontLookupTableBuilderTest, RepeatedScheduling) {
@@ -137,7 +159,15 @@
     font_lookup_table_builder_->ResetLookupTableForTesting();
     font_lookup_table_builder_->SetCachingEnabledForTesting(false);
     font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
-    font_lookup_table_builder_->EnsureFontUniqueNameTableForTesting();
+    bool test_callback_executed = false;
+    font_lookup_table_builder_->QueueShareMemoryRegionWhenReady(
+        base::SequencedTaskRunnerHandle::Get(),
+        base::BindLambdaForTesting(
+            [&test_callback_executed](base::ReadOnlySharedMemoryRegion) {
+              test_callback_executed = true;
+            }));
+    scoped_task_environment_.RunUntilIdle();
+    ASSERT_TRUE(test_callback_executed);
   }
 }
 
@@ -149,37 +179,54 @@
   // Cycle once to build cache file.
   font_lookup_table_builder_->ResetLookupTableForTesting();
   font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
-  ASSERT_TRUE(
-      font_lookup_table_builder_->EnsureFontUniqueNameTableForTesting());
-  // Truncate table for testing
-  base::FilePath cache_file_path = scoped_temp_dir_.GetPath().Append(
-      FILE_PATH_LITERAL("font_unique_name_table.pb"));
-  // Use FLAG_EXCLUSIVE_WRITE to block file and make persisting the cache fail
-  // as well, use FLAG_OPEN to ensure it got created by the table builder
-  // implementation.
-  base::File cache_file(cache_file_path, base::File::FLAG_OPEN |
-                                             base::File::FLAG_READ |
-                                             base::File::FLAG_WRITE |
-                                             base::File::FLAG_EXCLUSIVE_WRITE);
-  // Ensure the cache file was created in the empty scoped_temp_dir_ and has a
-  // non-zero length.
-  ASSERT_TRUE(cache_file.IsValid());
-  ASSERT_TRUE(cache_file.GetLength() > 0);
-  ASSERT_TRUE(cache_file.SetLength(cache_file.GetLength() / 2));
-  ASSERT_TRUE(cache_file.SetLength(cache_file.GetLength() * 2));
+
+  bool test_callback_executed = false;
+  base::File cache_file;
+  font_lookup_table_builder_->QueueShareMemoryRegionWhenReady(
+      base::SequencedTaskRunnerHandle::Get(),
+      base::BindLambdaForTesting([this, &cache_file, &test_callback_executed](
+                                     base::ReadOnlySharedMemoryRegion) {
+        ASSERT_TRUE(font_lookup_table_builder_->FontUniqueNameTableReady());
+        // Truncate table for testing
+        base::FilePath cache_file_path = scoped_temp_dir_.GetPath().Append(
+            FILE_PATH_LITERAL("font_unique_name_table.pb"));
+        // Use FLAG_EXCLUSIVE_WRITE to block file and make persisting the
+        // cache fail as well, use FLAG_OPEN to ensure it got created by the
+        // table builder implementation.
+        cache_file = base::File(cache_file_path,
+                                base::File::FLAG_OPEN | base::File::FLAG_READ |
+                                    base::File::FLAG_WRITE |
+                                    base::File::FLAG_EXCLUSIVE_WRITE);
+        // Ensure the cache file was created in the empty scoped_temp_dir_
+        // and has a non-zero length.
+        ASSERT_TRUE(cache_file.IsValid());
+        ASSERT_TRUE(cache_file.GetLength() > 0);
+        ASSERT_TRUE(cache_file.SetLength(cache_file.GetLength() / 2));
+        ASSERT_TRUE(cache_file.SetLength(cache_file.GetLength() * 2));
+        test_callback_executed = true;
+      }));
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(test_callback_executed);
 
   // Reload the cache file.
   font_lookup_table_builder_->ResetLookupTableForTesting();
   font_lookup_table_builder_->SchedulePrepareFontUniqueNameTableIfNeeded();
-  ASSERT_TRUE(
-      font_lookup_table_builder_->EnsureFontUniqueNameTableForTesting());
 
-  TestMatchFonts();
+  test_callback_executed = false;
+  font_lookup_table_builder_->QueueShareMemoryRegionWhenReady(
+      base::SequencedTaskRunnerHandle::Get(),
+      base::BindLambdaForTesting(
+          [this, &test_callback_executed](base::ReadOnlySharedMemoryRegion) {
+            TestMatchFonts();
+            test_callback_executed = true;
+          }));
 
-  // Ensure that the table is still valid even though persisting has failed due
-  // to the exclusive write lock on the file.
-  ASSERT_TRUE(
-      font_lookup_table_builder_->EnsureFontUniqueNameTableForTesting());
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(test_callback_executed);
+
+  // Ensure that the table is still valid even though persisting has failed
+  // due to the exclusive write lock on the file.
+  ASSERT_TRUE(font_lookup_table_builder_->FontUniqueNameTableReady());
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
index e7b2b92..a430751 100644
--- a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
+++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
@@ -17,6 +17,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "url/origin.h"
 
 namespace content {
@@ -145,9 +146,10 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!MediaStreamManager::IsOriginAllowed(render_process_id_,
                                            salt_and_origin.origin)) {
-    std::move(callback).Run(blink::MEDIA_DEVICE_INVALID_SECURITY_ORIGIN,
-                            std::string(), blink::MediaStreamDevices(),
-                            blink::MediaStreamDevices());
+    std::move(callback).Run(
+        blink::mojom::MediaStreamRequestResult::INVALID_SECURITY_ORIGIN,
+        std::string(), blink::MediaStreamDevices(),
+        blink::MediaStreamDevices());
     return;
   }
 
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
index c0c30efe..050faf2b 100644
--- a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
@@ -38,6 +38,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -94,7 +95,8 @@
                     int audio_array_size,
                     int video_array_size));
   MOCK_METHOD2(OnStreamGenerationFailure,
-               void(int request_id, blink::MediaStreamRequestResult result));
+               void(int request_id,
+                    blink::mojom::MediaStreamRequestResult result));
   MOCK_METHOD0(OnDeviceStopSuccess, void());
   MOCK_METHOD0(OnDeviceOpenSuccess, void());
 
@@ -153,11 +155,11 @@
  private:
   // These handler methods do minimal things and delegate to the mock methods.
   void OnStreamGenerated(int request_id,
-                         blink::MediaStreamRequestResult result,
+                         blink::mojom::MediaStreamRequestResult result,
                          const std::string& label,
                          const blink::MediaStreamDevices& audio_devices,
                          const blink::MediaStreamDevices& video_devices) {
-    if (result != blink::MEDIA_DEVICE_OK) {
+    if (result != blink::mojom::MediaStreamRequestResult::OK) {
       OnStreamGenerationFailed(request_id, result);
       return;
     }
@@ -178,7 +180,7 @@
   }
 
   void OnStreamGenerationFailed(int request_id,
-                                blink::MediaStreamRequestResult result) {
+                                blink::mojom::MediaStreamRequestResult result) {
     OnStreamGenerationFailure(request_id, result);
     if (!quit_closures_.empty()) {
       base::Closure quit_closure = quit_closures_.front();
@@ -350,7 +352,7 @@
   void GenerateStreamAndWaitForFailure(
       int page_request_id,
       const blink::StreamControls& controls,
-      blink::MediaStreamRequestResult expected_result) {
+      blink::mojom::MediaStreamRequestResult expected_result) {
     base::RunLoop run_loop;
     EXPECT_CALL(*host_,
                 OnStreamGenerationFailure(page_request_id, expected_result));
@@ -465,8 +467,9 @@
 TEST_F(MediaStreamDispatcherHostTest, GenerateStreamWithNothing) {
   blink::StreamControls controls(false, false);
 
-  GenerateStreamAndWaitForFailure(kPageRequestId, controls,
-                                  blink::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN);
+  GenerateStreamAndWaitForFailure(
+      kPageRequestId, controls,
+      blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN);
 }
 
 TEST_F(MediaStreamDispatcherHostTest, GenerateStreamWithAudioAndVideo) {
@@ -656,8 +659,9 @@
   blink::StreamControls controls(true, true);
   controls.video.device_id = "invalid source id";
 
-  GenerateStreamAndWaitForFailure(kPageRequestId, controls,
-                                  blink::MEDIA_DEVICE_NO_HARDWARE);
+  GenerateStreamAndWaitForFailure(
+      kPageRequestId, controls,
+      blink::mojom::MediaStreamRequestResult::NO_HARDWARE);
 }
 
 // Test that generating a stream with an invalid audio source id fail.
@@ -665,8 +669,9 @@
   blink::StreamControls controls(true, true);
   controls.audio.device_id = "invalid source id";
 
-  GenerateStreamAndWaitForFailure(kPageRequestId, controls,
-                                  blink::MEDIA_DEVICE_NO_HARDWARE);
+  GenerateStreamAndWaitForFailure(
+      kPageRequestId, controls,
+      blink::mojom::MediaStreamRequestResult::NO_HARDWARE);
 }
 
 TEST_F(MediaStreamDispatcherHostTest, GenerateStreamsNoAvailableVideoDevice) {
@@ -674,8 +679,9 @@
   blink::StreamControls controls(true, true);
 
   SetupFakeUI(false);
-  GenerateStreamAndWaitForFailure(kPageRequestId, controls,
-                                  blink::MEDIA_DEVICE_NO_HARDWARE);
+  GenerateStreamAndWaitForFailure(
+      kPageRequestId, controls,
+      blink::mojom::MediaStreamRequestResult::NO_HARDWARE);
 }
 
 // Test that if a OnStopStreamDevice message is received for a device that has
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index e0ef260..b5e8852 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -91,11 +91,11 @@
 
 using blink::MediaStreamDevice;
 using blink::MediaStreamDevices;
-using blink::MediaStreamRequestResult;
 using blink::MediaStreamRequestType;
 using blink::MediaStreamType;
 using blink::StreamControls;
 using blink::TrackControls;
+using blink::mojom::MediaStreamRequestResult;
 
 namespace {
 // Creates a random label used to identify requests.
@@ -410,7 +410,7 @@
   void RunMojoCallbacks() {
     if (generate_stream_cb) {
       std::move(generate_stream_cb)
-          .Run(blink::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN, std::string(),
+          .Run(MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN, std::string(),
                MediaStreamDevices(), MediaStreamDevices());
     }
 
@@ -708,7 +708,8 @@
     if (std::move(generate_stream_test_callback_).Run(controls)) {
       FinalizeGenerateStream(label, request);
     } else {
-      FinalizeRequestFailed(label, request, blink::MEDIA_DEVICE_INVALID_STATE);
+      FinalizeRequestFailed(label, request,
+                            MediaStreamRequestResult::INVALID_STATE);
     }
     return;
   }
@@ -1222,7 +1223,7 @@
       request->video_type() == blink::MEDIA_DISPLAY_VIDEO_CAPTURE;
   if (is_display_capture && !SetUpDisplayCaptureRequest(request)) {
     FinalizeRequestFailed(label, request,
-                          blink::MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE);
+                          MediaStreamRequestResult::SCREEN_CAPTURE_FAILURE);
     return;
   }
 
@@ -1232,7 +1233,7 @@
   if (is_tab_capture) {
     if (!SetUpTabCaptureRequest(request, label)) {
       FinalizeRequestFailed(label, request,
-                            blink::MEDIA_DEVICE_TAB_CAPTURE_FAILURE);
+                            MediaStreamRequestResult::TAB_CAPTURE_FAILURE);
     }
     return;
   }
@@ -1241,7 +1242,7 @@
       request->video_type() == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE;
   if (is_screen_capture && !SetUpScreenCaptureRequest(request)) {
     FinalizeRequestFailed(label, request,
-                          blink::MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE);
+                          MediaStreamRequestResult::SCREEN_CAPTURE_FAILURE);
     return;
   }
 
@@ -1254,7 +1255,8 @@
     // If no actual device capture is requested, set up the request with an
     // empty device list.
     if (!SetUpDeviceCaptureRequest(request, MediaDeviceEnumeration())) {
-      FinalizeRequestFailed(label, request, blink::MEDIA_DEVICE_NO_HARDWARE);
+      FinalizeRequestFailed(label, request,
+                            MediaStreamRequestResult::NO_HARDWARE);
       return;
     }
   }
@@ -1374,7 +1376,7 @@
   // Received invalid device id.
   if (device_id.type != content::DesktopMediaID::TYPE_WEB_CONTENTS) {
     FinalizeRequestFailed(label, request,
-                          blink::MEDIA_DEVICE_TAB_CAPTURE_FAILURE);
+                          MediaStreamRequestResult::TAB_CAPTURE_FAILURE);
     return;
   }
 
@@ -1505,7 +1507,7 @@
   }
 
   std::move(request->generate_stream_cb)
-      .Run(blink::MEDIA_DEVICE_OK, label, audio_devices, video_devices);
+      .Run(MediaStreamRequestResult::OK, label, audio_devices, video_devices);
 }
 
 void MediaStreamManager::FinalizeRequestFailed(
@@ -1754,7 +1756,8 @@
   }
 
   if (!SetUpDeviceCaptureRequest(request, enumeration))
-    FinalizeRequestFailed(label, request, blink::MEDIA_DEVICE_NO_HARDWARE);
+    FinalizeRequestFailed(label, request,
+                          MediaStreamRequestResult::NO_HARDWARE);
   else
     ReadOutputParamsAndPostRequestToUI(label, request, enumeration);
 }
@@ -1835,7 +1838,7 @@
   }
 
   // Handle the case when the request was denied.
-  if (result != blink::MEDIA_DEVICE_OK) {
+  if (result != MediaStreamRequestResult::OK) {
     FinalizeRequestFailed(label, request, result);
     return;
   }
diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h
index 5c5b449..350ae29 100644
--- a/content/browser/renderer_host/media/media_stream_manager.h
+++ b/content/browser/renderer_host/media/media_stream_manager.h
@@ -54,6 +54,7 @@
 #include "third_party/blink/public/common/mediastream/media_devices.h"
 #include "third_party/blink/public/common/mediastream/media_stream_controls.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 namespace media {
 class AudioSystem;
@@ -86,7 +87,7 @@
                               std::unique_ptr<MediaStreamUIProxy> ui)>;
 
   using GenerateStreamCallback =
-      base::OnceCallback<void(blink::MediaStreamRequestResult result,
+      base::OnceCallback<void(blink::mojom::MediaStreamRequestResult result,
                               const std::string& label,
                               const blink::MediaStreamDevices& audio_devices,
                               const blink::MediaStreamDevices& video_devices)>;
@@ -363,7 +364,7 @@
       const std::string& label,
       const media::AudioParameters& output_parameters,
       const blink::MediaStreamDevices& devices,
-      blink::MediaStreamRequestResult result);
+      blink::mojom::MediaStreamRequestResult result);
   void HandleChangeSourceRequestResponse(
       const std::string& label,
       DeviceRequest* request,
@@ -447,7 +448,7 @@
   void FinalizeGenerateStream(const std::string& label, DeviceRequest* request);
   void FinalizeRequestFailed(const std::string& label,
                              DeviceRequest* request,
-                             blink::MediaStreamRequestResult result);
+                             blink::mojom::MediaStreamRequestResult result);
   void FinalizeOpenDevice(const std::string& label, DeviceRequest* request);
   void FinalizeChangeDevice(const std::string& label, DeviceRequest* request);
   void FinalizeMediaAccessRequest(const std::string& label,
diff --git a/content/browser/renderer_host/media/media_stream_manager_unittest.cc b/content/browser/renderer_host/media/media_stream_manager_unittest.cc
index 2c66686..8a8abe5 100644
--- a/content/browser/renderer_host/media/media_stream_manager_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_manager_unittest.cc
@@ -259,7 +259,8 @@
             [](base::RunLoop* wait_loop, bool request_audio,
                blink::MediaStreamDevice* audio_device,
                blink::MediaStreamDevice* video_device,
-               blink::MediaStreamRequestResult result, const std::string& label,
+               blink::mojom::MediaStreamRequestResult result,
+               const std::string& label,
                const blink::MediaStreamDevices& audio_devices,
                const blink::MediaStreamDevices& video_devices) {
               if (request_audio) {
@@ -541,7 +542,7 @@
   controls.video.stream_type = blink::MEDIA_DISPLAY_VIDEO_CAPTURE;
 
   MediaStreamManager::GenerateStreamCallback generate_stream_callback =
-      base::BindOnce([](blink::MediaStreamRequestResult result,
+      base::BindOnce([](blink::mojom::MediaStreamRequestResult result,
                         const std::string& label,
                         const blink::MediaStreamDevices& audio_devices,
                         const blink::MediaStreamDevices& video_devices) {});
@@ -584,7 +585,8 @@
   MediaStreamManager::GenerateStreamCallback generate_stream_callback =
       base::BindOnce(
           [](base::RunLoop* wait_loop, blink::MediaStreamDevice* video_device,
-             blink::MediaStreamRequestResult result, const std::string& label,
+             blink::mojom::MediaStreamRequestResult result,
+             const std::string& label,
              const blink::MediaStreamDevices& audio_devices,
              const blink::MediaStreamDevices& video_devices) {
             EXPECT_EQ(0u, audio_devices.size());
@@ -641,7 +643,8 @@
   MediaStreamManager::GenerateStreamCallback generate_stream_callback =
       base::BindOnce(
           [](base::RunLoop* wait_loop, blink::MediaStreamDevice* video_device,
-             blink::MediaStreamRequestResult result, const std::string& label,
+             blink::mojom::MediaStreamRequestResult result,
+             const std::string& label,
              const blink::MediaStreamDevices& audio_devices,
              const blink::MediaStreamDevices& video_devices) {
             EXPECT_EQ(0u, audio_devices.size());
diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy.cc b/content/browser/renderer_host/media/media_stream_ui_proxy.cc
index c8209bd..91b4efa 100644
--- a/content/browser/renderer_host/media/media_stream_ui_proxy.cc
+++ b/content/browser/renderer_host/media/media_stream_ui_proxy.cc
@@ -69,11 +69,12 @@
   void RequestAccess(std::unique_ptr<MediaStreamRequest> request);
   void OnStarted(gfx::NativeViewId* window_id, bool has_source_callback);
 
-  void ProcessAccessRequestResponse(int render_process_id,
-                                    int render_frame_id,
-                                    const blink::MediaStreamDevices& devices,
-                                    blink::MediaStreamRequestResult result,
-                                    std::unique_ptr<MediaStreamUI> stream_ui);
+  void ProcessAccessRequestResponse(
+      int render_process_id,
+      int render_frame_id,
+      const blink::MediaStreamDevices& devices,
+      blink::mojom::MediaStreamRequestResult result,
+      std::unique_ptr<MediaStreamUI> stream_ui);
 
  private:
   friend class FakeMediaStreamUIProxy;
@@ -117,7 +118,8 @@
   if (!render_delegate) {
     ProcessAccessRequestResponse(
         request->render_process_id, request->render_frame_id,
-        blink::MediaStreamDevices(), blink::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN,
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN,
         std::unique_ptr<MediaStreamUI>());
     return;
   }
@@ -151,7 +153,7 @@
     int render_process_id,
     int render_frame_id,
     const blink::MediaStreamDevices& devices,
-    blink::MediaStreamRequestResult result,
+    blink::mojom::MediaStreamRequestResult result,
     std::unique_ptr<MediaStreamUI> stream_ui) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
@@ -173,8 +175,9 @@
 
     filtered_devices.push_back(device);
   }
-  if (filtered_devices.empty() && result == blink::MEDIA_DEVICE_OK)
-    result = blink::MEDIA_DEVICE_PERMISSION_DENIED;
+  if (filtered_devices.empty() &&
+      result == blink::mojom::MediaStreamRequestResult::OK)
+    result = blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
 
   if (stream_ui)
     ui_ = std::move(stream_ui);
@@ -269,7 +272,7 @@
 
 void MediaStreamUIProxy::ProcessAccessRequestResponse(
     const blink::MediaStreamDevices& devices,
-    blink::MediaStreamRequestResult result) {
+    blink::mojom::MediaStreamRequestResult result) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!response_callback_.is_null());
 
@@ -330,12 +333,12 @@
     // Immediately deny the request.
     base::PostTaskWithTraits(
         FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&MediaStreamUIProxy::Core::ProcessAccessRequestResponse,
-                       base::Unretained(core_.get()),
-                       request->render_process_id, request->render_frame_id,
-                       blink::MediaStreamDevices(),
-                       blink::MEDIA_DEVICE_PERMISSION_DENIED,
-                       std::unique_ptr<MediaStreamUI>()));
+        base::BindOnce(
+            &MediaStreamUIProxy::Core::ProcessAccessRequestResponse,
+            base::Unretained(core_.get()), request->render_process_id,
+            request->render_frame_id, blink::MediaStreamDevices(),
+            blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+            std::unique_ptr<MediaStreamUI>()));
     return;
   }
 
@@ -374,8 +377,9 @@
       base::BindOnce(&MediaStreamUIProxy::Core::ProcessAccessRequestResponse,
                      base::Unretained(core_.get()), request->render_process_id,
                      request->render_frame_id, devices_to_use,
-                     devices_to_use.empty() ? blink::MEDIA_DEVICE_NO_HARDWARE
-                                            : blink::MEDIA_DEVICE_OK,
+                     devices_to_use.empty()
+                         ? blink::mojom::MediaStreamRequestResult::NO_HARDWARE
+                         : blink::mojom::MediaStreamRequestResult::OK,
                      std::unique_ptr<MediaStreamUI>()));
 }
 
diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy.h b/content/browser/renderer_host/media/media_stream_ui_proxy.h
index 701df254..c406aab 100644
--- a/content/browser/renderer_host/media/media_stream_ui_proxy.h
+++ b/content/browser/renderer_host/media/media_stream_ui_proxy.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace content {
@@ -26,7 +27,7 @@
  public:
   using ResponseCallback =
       base::OnceCallback<void(const blink::MediaStreamDevices& devices,
-                              blink::MediaStreamRequestResult result)>;
+                              blink::mojom::MediaStreamRequestResult result)>;
 
   using WindowIdCallback =
       base::OnceCallback<void(gfx::NativeViewId window_id)>;
@@ -64,8 +65,9 @@
   friend class Core;
   friend class FakeMediaStreamUIProxy;
 
-  void ProcessAccessRequestResponse(const blink::MediaStreamDevices& devices,
-                                    blink::MediaStreamRequestResult result);
+  void ProcessAccessRequestResponse(
+      const blink::MediaStreamDevices& devices,
+      blink::mojom::MediaStreamRequestResult result);
   void ProcessStopRequestFromUI();
   void ProcessChangeSourceRequestFromUI();
   void OnWindowId(WindowIdCallback window_id_callback,
diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc b/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
index 3b707bd..194b299 100644
--- a/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
@@ -48,7 +48,7 @@
  public:
   MOCK_METHOD2(OnAccessRequestResponse,
                void(const blink::MediaStreamDevices& devices,
-                    blink::MediaStreamRequestResult result));
+                    blink::mojom::MediaStreamRequestResult result));
   MOCK_METHOD1(OnCheckResponse, void(bool have_access));
 };
 
@@ -130,7 +130,7 @@
   ASSERT_FALSE(callback.is_null());
 
   blink::MediaStreamDevices devices;
-  std::move(callback).Run(devices, blink::MEDIA_DEVICE_OK,
+  std::move(callback).Run(devices, blink::mojom::MediaStreamRequestResult::OK,
                           std::unique_ptr<MediaStreamUI>());
 
   blink::MediaStreamDevices response;
@@ -165,7 +165,8 @@
                                              "Mic", "Mic"));
   auto ui = std::make_unique<MockMediaStreamUI>();
   EXPECT_CALL(*ui, MockOnStarted(_, _)).WillOnce(Return(0));
-  std::move(callback).Run(devices, blink::MEDIA_DEVICE_OK, std::move(ui));
+  std::move(callback).Run(devices, blink::mojom::MediaStreamRequestResult::OK,
+                          std::move(ui));
 
   blink::MediaStreamDevices response;
   EXPECT_CALL(response_callback_, OnAccessRequestResponse(_, _))
@@ -203,7 +204,8 @@
 
   blink::MediaStreamDevices devices;
   std::unique_ptr<MediaStreamUI> ui;
-  std::move(callback).Run(devices, blink::MEDIA_DEVICE_OK, std::move(ui));
+  std::move(callback).Run(devices, blink::mojom::MediaStreamRequestResult::OK,
+                          std::move(ui));
 }
 
 TEST_F(MediaStreamUIProxyTest, StopFromUI) {
@@ -233,7 +235,8 @@
   auto ui = std::make_unique<MockMediaStreamUI>();
   EXPECT_CALL(*ui, MockOnStarted(_, _))
       .WillOnce(testing::DoAll(SaveArg<0>(&stop_callback), Return(0)));
-  std::move(callback).Run(devices, blink::MEDIA_DEVICE_OK, std::move(ui));
+  std::move(callback).Run(devices, blink::mojom::MediaStreamRequestResult::OK,
+                          std::move(ui));
 
   blink::MediaStreamDevices response;
   EXPECT_CALL(response_callback_, OnAccessRequestResponse(_, _))
@@ -278,7 +281,8 @@
   auto ui = std::make_unique<MockMediaStreamUI>();
   EXPECT_CALL(*ui, MockOnStarted(_, _)).WillOnce(Return(kWindowId));
 
-  std::move(callback).Run(blink::MediaStreamDevices(), blink::MEDIA_DEVICE_OK,
+  std::move(callback).Run(blink::MediaStreamDevices(),
+                          blink::mojom::MediaStreamRequestResult::OK,
                           std::move(ui));
   EXPECT_CALL(response_callback_, OnAccessRequestResponse(_, _));
 
@@ -321,7 +325,8 @@
   auto ui = std::make_unique<MockMediaStreamUI>();
   EXPECT_CALL(*ui, MockOnStarted(_, _))
       .WillOnce(testing::DoAll(SaveArg<1>(&source_callback), Return(0)));
-  std::move(callback).Run(devices, blink::MEDIA_DEVICE_OK, std::move(ui));
+  std::move(callback).Run(devices, blink::mojom::MediaStreamRequestResult::OK,
+                          std::move(ui));
 
   blink::MediaStreamDevices response;
   EXPECT_CALL(response_callback_, OnAccessRequestResponse(_, _))
@@ -374,7 +379,7 @@
 
   void GetResultForRequest(std::unique_ptr<MediaStreamRequest> request,
                            blink::MediaStreamDevices* devices_out,
-                           blink::MediaStreamRequestResult* result_out) {
+                           blink::mojom::MediaStreamRequestResult* result_out) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     base::RunLoop run_loop;
     quit_closure_ = run_loop.QuitClosure();
@@ -412,7 +417,8 @@
             blink::MEDIA_DEVICE_VIDEO_CAPTURE, "Camera", "Camera"));
       }
       auto ui = std::make_unique<MockMediaStreamUI>();
-      std::move(callback).Run(devices, blink::MEDIA_DEVICE_OK, std::move(ui));
+      std::move(callback).Run(
+          devices, blink::mojom::MediaStreamRequestResult::OK, std::move(ui));
     }
   };
 
@@ -427,8 +433,9 @@
             base::Unretained(this)));
   }
 
-  void FinishedGetResultOnIOThread(const blink::MediaStreamDevices& devices,
-                                   blink::MediaStreamRequestResult result) {
+  void FinishedGetResultOnIOThread(
+      const blink::MediaStreamDevices& devices,
+      blink::mojom::MediaStreamRequestResult result) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     proxy_.reset();
     base::PostTaskWithTraits(
@@ -438,7 +445,7 @@
   }
 
   void FinishedGetResult(const blink::MediaStreamDevices& devices,
-                         blink::MediaStreamRequestResult result) {
+                         blink::mojom::MediaStreamRequestResult result) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     devices_ = devices;
     result_ = result;
@@ -447,7 +454,7 @@
 
   // These should only be accessed on the UI thread.
   blink::MediaStreamDevices devices_;
-  blink::MediaStreamRequestResult result_;
+  blink::mojom::MediaStreamRequestResult result_;
   base::Closure quit_closure_;
 
   // These should only be accessed on the IO thread.
@@ -457,14 +464,14 @@
 
 TEST_F(MediaStreamUIProxyFeaturePolicyTest, FeaturePolicy) {
   blink::MediaStreamDevices devices;
-  blink::MediaStreamRequestResult result;
+  blink::mojom::MediaStreamRequestResult result;
 
   // Default FP.
   GetResultForRequest(
       CreateRequest(main_rfh(), blink::MEDIA_DEVICE_AUDIO_CAPTURE,
                     blink::MEDIA_DEVICE_VIDEO_CAPTURE),
       &devices, &result);
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   ASSERT_EQ(2u, devices.size());
   EXPECT_EQ(blink::MEDIA_DEVICE_AUDIO_CAPTURE, devices[0].type);
   EXPECT_EQ(blink::MEDIA_DEVICE_VIDEO_CAPTURE, devices[1].type);
@@ -477,7 +484,7 @@
       CreateRequest(main_rfh(), blink::MEDIA_DEVICE_AUDIO_CAPTURE,
                     blink::MEDIA_DEVICE_VIDEO_CAPTURE),
       &devices, &result);
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   ASSERT_EQ(1u, devices.size());
   EXPECT_EQ(blink::MEDIA_DEVICE_VIDEO_CAPTURE, devices[0].type);
 
@@ -489,7 +496,7 @@
       CreateRequest(main_rfh(), blink::MEDIA_DEVICE_AUDIO_CAPTURE,
                     blink::MEDIA_DEVICE_VIDEO_CAPTURE),
       &devices, &result);
-  EXPECT_EQ(blink::MEDIA_DEVICE_OK, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   ASSERT_EQ(1u, devices.size());
   EXPECT_EQ(blink::MEDIA_DEVICE_AUDIO_CAPTURE, devices[0].type);
 
@@ -500,7 +507,7 @@
   GetResultForRequest(CreateRequest(main_rfh(), blink::MEDIA_NO_SERVICE,
                                     blink::MEDIA_DEVICE_VIDEO_CAPTURE),
                       &devices, &result);
-  EXPECT_EQ(blink::MEDIA_DEVICE_PERMISSION_DENIED, result);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, result);
   ASSERT_EQ(0u, devices.size());
 }
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 7940d99..515e148c 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -134,7 +134,6 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/site_instance_impl.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/browser/streams/stream_context.h"
 #include "content/browser/tracing/trace_message_filter.h"
 #include "content/browser/webrtc/webrtc_internals.h"
 #include "content/browser/websockets/websocket_manager.h"
diff --git a/content/browser/resource_context_impl.cc b/content/browser/resource_context_impl.cc
index 261e934..434549d 100644
--- a/content/browser/resource_context_impl.cc
+++ b/content/browser/resource_context_impl.cc
@@ -11,7 +11,6 @@
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/loader/resource_request_info_impl.h"
-#include "content/browser/streams/stream_context.h"
 #include "content/browser/webui/url_data_manager_backend.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
@@ -21,7 +20,6 @@
 namespace content {
 
 // Key names on ResourceContext.
-const char kStreamContextKeyName[] = "content_stream_context";
 const char kURLDataManagerBackendKeyName[] = "url_data_manager_backend";
 
 ResourceContext::ResourceContext() {}
@@ -38,13 +36,6 @@
       resource_context, kBlobStorageContextKeyName);
 }
 
-StreamContext* GetStreamContextForResourceContext(
-    const ResourceContext* resource_context) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  return UserDataAdapter<StreamContext>::Get(
-      resource_context, kStreamContextKeyName);
-}
-
 URLDataManagerBackend* GetURLDataManagerForResourceContext(
     ResourceContext* context) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -64,10 +55,6 @@
       std::make_unique<UserDataAdapter<ChromeBlobStorageContext>>(
           ChromeBlobStorageContext::GetFor(browser_context)));
 
-  resource_context->SetUserData(
-      kStreamContextKeyName, std::make_unique<UserDataAdapter<StreamContext>>(
-                                 StreamContext::GetFor(browser_context)));
-
   resource_context->DetachFromSequence();
 }
 
diff --git a/content/browser/resource_context_impl.h b/content/browser/resource_context_impl.h
index 903cc54..2baade5 100644
--- a/content/browser/resource_context_impl.h
+++ b/content/browser/resource_context_impl.h
@@ -11,7 +11,6 @@
 namespace content {
 
 class ChromeBlobStorageContext;
-class StreamContext;
 class BrowserContext;
 class URLDataManagerBackend;
 
@@ -22,9 +21,6 @@
 ChromeBlobStorageContext* GetChromeBlobStorageContextForResourceContext(
     const ResourceContext* resource_context);
 
-CONTENT_EXPORT StreamContext* GetStreamContextForResourceContext(
-    const ResourceContext* resource_context);
-
 URLDataManagerBackend* GetURLDataManagerForResourceContext(
     ResourceContext* context);
 
diff --git a/content/browser/service_worker/service_worker_new_script_loader.cc b/content/browser/service_worker/service_worker_new_script_loader.cc
index 7099767..3e9079c 100644
--- a/content/browser/service_worker/service_worker_new_script_loader.cc
+++ b/content/browser/service_worker/service_worker_new_script_loader.cc
@@ -31,6 +31,20 @@
 // We chose this size because the AppCache uses this.
 const uint32_t ServiceWorkerNewScriptLoader::kReadBufferSize = 32768;
 
+// This is for debugging https://crbug.com/959627.
+// The purpose is to see where the IOBuffer comes from by checking |__vfptr|.
+class ServiceWorkerNewScriptLoader::WrappedIOBuffer
+    : public net::WrappedIOBuffer {
+ public:
+  WrappedIOBuffer(const char* data) : net::WrappedIOBuffer(data) {}
+
+ private:
+  ~WrappedIOBuffer() override = default;
+
+  // This is to make sure that the vtable is not merged with other classes.
+  virtual void dummy() { NOTREACHED(); }
+};
+
 std::unique_ptr<ServiceWorkerNewScriptLoader>
 ServiceWorkerNewScriptLoader::CreateForNetworkOnly(
     int32_t routing_id,
@@ -717,8 +731,7 @@
   // next time.
   uint32_t bytes_written = std::min<uint32_t>(kReadBufferSize, bytes_available);
 
-  auto buffer =
-      base::MakeRefCounted<net::WrappedIOBuffer>(pending_buffer->buffer());
+  auto buffer = base::MakeRefCounted<WrappedIOBuffer>(pending_buffer->buffer());
   MojoResult result = client_producer_->WriteData(
       buffer->data(), &bytes_written, MOJO_WRITE_DATA_FLAG_NONE);
   switch (result) {
@@ -747,8 +760,7 @@
   net::Error error = cache_writer_->MaybeWriteData(
       buffer.get(), base::strict_cast<size_t>(bytes_written),
       base::BindOnce(&ServiceWorkerNewScriptLoader::OnWriteDataComplete,
-                     weak_factory_.GetWeakPtr(),
-                     base::WrapRefCounted(pending_buffer.get()),
+                     weak_factory_.GetWeakPtr(), pending_buffer,
                      bytes_written));
   if (error == net::ERR_IO_PENDING) {
     // OnWriteDataComplete() will be called asynchronously.
diff --git a/content/browser/service_worker/service_worker_new_script_loader.h b/content/browser/service_worker/service_worker_new_script_loader.h
index ec46aba..36cff0d 100644
--- a/content/browser/service_worker/service_worker_new_script_loader.h
+++ b/content/browser/service_worker/service_worker_new_script_loader.h
@@ -169,6 +169,8 @@
   const static uint32_t kReadBufferSize;
 
  private:
+  class WrappedIOBuffer;
+
   // This is for constructing network-only script loaders.
   // |loader_factory| is used to load the script, see class comments.
   ServiceWorkerNewScriptLoader(
diff --git a/content/browser/service_worker/service_worker_register_job.cc b/content/browser/service_worker/service_worker_register_job.cc
index 67059c6..b24403e 100644
--- a/content/browser/service_worker/service_worker_register_job.cc
+++ b/content/browser/service_worker/service_worker_register_job.cc
@@ -349,12 +349,27 @@
              : UpdateCheckType::kMainScriptDuringStartWorker;
 }
 
-void ServiceWorkerRegisterJob::OnUpdateCheckFinished(bool script_changed) {
+void ServiceWorkerRegisterJob::OnUpdateCheckFinished(
+    ServiceWorkerSingleScriptUpdateChecker::Result result,
+    std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::FailureInfo>
+        failure_info) {
   DCHECK_EQ(GetUpdateCheckType(),
             UpdateCheckType::kAllScriptsBeforeStartWorker);
+
+  // Update check failed.
+  if (result == ServiceWorkerSingleScriptUpdateChecker::Result::kFailed) {
+    DCHECK(failure_info);
+    ResolvePromise(failure_info->status, failure_info->error_message, nullptr);
+    // This terminates the current job (|this|).
+    Complete(failure_info->status, failure_info->error_message);
+    return;
+  }
+
+  // Update the last update check time.
   BumpLastUpdateCheckTimeIfNeeded();
-  if (!script_changed) {
-    // TODO(momohatt): Set phase correctly.
+
+  // Update check succeeded.
+  if (result == ServiceWorkerSingleScriptUpdateChecker::Result::kIdentical) {
     ResolvePromise(blink::ServiceWorkerStatusCode::kOk, std::string(),
                    registration());
     // This terminates the current job (|this|).
diff --git a/content/browser/service_worker/service_worker_register_job.h b/content/browser/service_worker/service_worker_register_job.h
index 4f6dc40..0c50060c 100644
--- a/content/browser/service_worker/service_worker_register_job.h
+++ b/content/browser/service_worker/service_worker_register_job.h
@@ -125,9 +125,12 @@
   UpdateCheckType GetUpdateCheckType() const;
 
   // This method is only called when ServiceWorkerImportedScriptUpdateCheck is
-  // enabled. When some script changed, the parameter |script_changed| is set
-  // to true.
-  void OnUpdateCheckFinished(bool script_changed);
+  // enabled. Refer ServiceWorkerUpdateChecker::UpdateStatusCallback for the
+  // meaning of the parameters.
+  void OnUpdateCheckFinished(
+      ServiceWorkerSingleScriptUpdateChecker::Result result,
+      std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::FailureInfo>
+          failure_info);
 
   void RegisterAndContinue();
   void ContinueWithUninstallingRegistration(
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.cc b/content/browser/service_worker/service_worker_single_script_update_checker.cc
index 959664e..c300cf105 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker.cc
+++ b/content/browser/service_worker/service_worker_single_script_update_checker.cc
@@ -7,6 +7,8 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/strings/stringprintf.h"
+#include "components/network_session_configurator/common/network_switches.h"
 #include "content/browser/appcache/appcache_response.h"
 #include "content/browser/service_worker/service_worker_cache_writer.h"
 #include "content/common/service_worker/service_worker_utils.h"
@@ -67,9 +69,24 @@
 
 namespace content {
 
+// This is for debugging https://crbug.com/959627.
+// The purpose is to see where the IOBuffer comes from by checking |__vfptr|.
+class ServiceWorkerSingleScriptUpdateChecker::WrappedIOBuffer
+    : public net::WrappedIOBuffer {
+ public:
+  WrappedIOBuffer(const char* data) : net::WrappedIOBuffer(data) {}
+
+ private:
+  ~WrappedIOBuffer() override = default;
+
+  // This is to make sure that the vtable is not merged with other classes.
+  virtual void dummy() { NOTREACHED(); }
+};
+
 ServiceWorkerSingleScriptUpdateChecker::ServiceWorkerSingleScriptUpdateChecker(
     const GURL& url,
     bool is_main_script,
+    const GURL& scope,
     bool force_bypass_cache,
     blink::mojom::ServiceWorkerUpdateViaCache update_via_cache,
     base::TimeDelta time_since_last_check,
@@ -79,6 +96,8 @@
     std::unique_ptr<ServiceWorkerResponseWriter> writer,
     ResultCallback callback)
     : script_url_(url),
+      is_main_script_(is_main_script),
+      scope_(scope),
       force_bypass_cache_(force_bypass_cache),
       update_via_cache_(update_via_cache),
       time_since_last_check_(time_since_last_check),
@@ -93,11 +112,11 @@
   resource_request.resource_type = static_cast<int>(
       is_main_script ? ResourceType::kServiceWorker : ResourceType::kScript);
   resource_request.do_not_prompt_for_login = true;
-  if (is_main_script)
+  if (is_main_script_)
     resource_request.headers.SetHeader("Service-Worker", "script");
 
   if (ServiceWorkerUtils::ShouldValidateBrowserCacheForScript(
-          is_main_script, force_bypass_cache_, update_via_cache_,
+          is_main_script_, force_bypass_cache_, update_via_cache_,
           time_since_last_check_)) {
     resource_request.load_flags |= net::LOAD_VALIDATE_CACHE;
   }
@@ -143,11 +162,53 @@
   response_info->connection_info = response_head.connection_info;
   response_info->remote_endpoint = response_head.remote_endpoint;
 
-  // TODO(momohatt): Check for header errors.
-
   network_loader_state_ =
       ServiceWorkerNewScriptLoader::NetworkLoaderState::kWaitingForBody;
   network_accessed_ = response_head.network_accessed;
+
+  if (response_head.headers->response_code() / 100 != 2) {
+    std::string error_message =
+        base::StringPrintf(kServiceWorkerBadHTTPResponseError,
+                           response_head.headers->response_code());
+    Fail(blink::ServiceWorkerStatusCode::kErrorNetwork, error_message);
+    return;
+  }
+
+  // Check the certificate error.
+  if (net::IsCertStatusError(response_head.cert_status) &&
+      !base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kIgnoreCertificateErrors)) {
+    Fail(blink::ServiceWorkerStatusCode::kErrorSecurity,
+         kServiceWorkerSSLError);
+    return;
+  }
+
+  if (!blink::IsSupportedJavascriptMimeType(response_head.mime_type)) {
+    std::string error_message =
+        response_head.mime_type.empty()
+            ? kServiceWorkerNoMIMEError
+            : base::StringPrintf(kServiceWorkerBadMIMEError,
+                                 response_head.mime_type.c_str());
+    Fail(blink::ServiceWorkerStatusCode::kErrorSecurity, error_message);
+    return;
+  }
+
+  // Check the path restriction defined in the spec:
+  // https://w3c.github.io/ServiceWorker/#service-worker-script-response
+  // Only main script needs the following check.
+  if (is_main_script_) {
+    std::string service_worker_allowed;
+    bool has_header = response_head.headers->EnumerateHeader(
+        nullptr, kServiceWorkerAllowed, &service_worker_allowed);
+    std::string error_message;
+    if (!ServiceWorkerUtils::IsPathRestrictionSatisfied(
+            scope_, script_url_, has_header ? &service_worker_allowed : nullptr,
+            &error_message)) {
+      Fail(blink::ServiceWorkerStatusCode::kErrorSecurity, error_message);
+      return;
+    }
+  }
+
   WriteHeaders(
       base::MakeRefCounted<HttpResponseInfoIOBuffer>(std::move(response_info)));
 }
@@ -192,7 +253,8 @@
   network_loader_state_ =
       ServiceWorkerNewScriptLoader::NetworkLoaderState::kCompleted;
   if (status.error_code != net::OK) {
-    Finish(Result::kFailed);
+    Fail(blink::ServiceWorkerStatusCode::kErrorNetwork,
+         kServiceWorkerFetchScriptError);
     return;
   }
 
@@ -241,7 +303,7 @@
       case ServiceWorkerNewScriptLoader::WriterState::kCompleted:
         DCHECK_EQ(header_writer_state_,
                   ServiceWorkerNewScriptLoader::WriterState::kCompleted);
-        Finish(Result::kIdentical);
+        Succeed(Result::kIdentical);
         return;
     }
   }
@@ -278,7 +340,8 @@
   DCHECK_NE(error, net::ERR_IO_PENDING);
   header_writer_state_ = ServiceWorkerNewScriptLoader::WriterState::kCompleted;
   if (error != net::OK) {
-    Finish(Result::kFailed);
+    Fail(blink::ServiceWorkerStatusCode::kErrorFailed,
+         kServiceWorkerFetchScriptError);
     return;
   }
 
@@ -362,7 +425,7 @@
 void ServiceWorkerSingleScriptUpdateChecker::CompareData(
     scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,
     uint32_t bytes_to_compare) {
-  auto buffer = base::MakeRefCounted<net::WrappedIOBuffer>(
+  auto buffer = base::MakeRefCounted<WrappedIOBuffer>(
       pending_buffer ? pending_buffer->buffer() : nullptr);
 
   // Compare the network data and the stored data.
@@ -370,8 +433,7 @@
       buffer.get(), bytes_to_compare,
       base::BindOnce(
           &ServiceWorkerSingleScriptUpdateChecker::OnCompareDataComplete,
-          weak_factory_.GetWeakPtr(),
-          base::WrapRefCounted(pending_buffer.get()), bytes_to_compare));
+          weak_factory_.GetWeakPtr(), pending_buffer, bytes_to_compare));
 
   if (pending_buffer) {
     pending_buffer->CompleteRead(bytes_to_compare);
@@ -395,31 +457,47 @@
     // |cache_writer_| can be pausing only when it finds difference between
     // stored body and network body.
     DCHECK_EQ(error, net::ERR_IO_PENDING);
-    Finish(Result::kDifferent);
+    Succeed(Result::kDifferent);
     return;
   }
   if (!pending_buffer || error != net::OK) {
-    Finish(Result::kIdentical);
+    Succeed(Result::kIdentical);
     return;
   }
   DCHECK(pending_buffer);
   network_watcher_.ArmOrNotify();
 }
 
-void ServiceWorkerSingleScriptUpdateChecker::Finish(Result result) {
+void ServiceWorkerSingleScriptUpdateChecker::Fail(
+    blink::ServiceWorkerStatusCode status,
+    const std::string& error_message) {
+  Finish(Result::kFailed, std::make_unique<FailureInfo>(status, error_message));
+}
+
+void ServiceWorkerSingleScriptUpdateChecker::Succeed(Result result) {
+  DCHECK_NE(result, Result::kFailed);
+  Finish(result, nullptr);
+}
+
+void ServiceWorkerSingleScriptUpdateChecker::Finish(
+    Result result,
+    std::unique_ptr<FailureInfo> failure_info) {
   network_watcher_.Cancel();
   if (Result::kDifferent == result) {
     auto paused_state = std::make_unique<PausedState>(
         std::move(cache_writer_), std::move(network_loader_),
         network_client_binding_.Unbind(), std::move(network_consumer_),
         network_loader_state_, body_writer_state_);
-    std::move(callback_).Run(script_url_, result, std::move(paused_state));
+    std::move(callback_).Run(script_url_, result, nullptr,
+                             std::move(paused_state));
     return;
   }
+
   network_loader_.reset();
   network_client_binding_.Close();
   network_consumer_.reset();
-  std::move(callback_).Run(script_url_, result, nullptr);
+  std::move(callback_).Run(script_url_, result, std::move(failure_info),
+                           nullptr);
 }
 
 ServiceWorkerSingleScriptUpdateChecker::PausedState::PausedState(
@@ -438,4 +516,10 @@
 
 ServiceWorkerSingleScriptUpdateChecker::PausedState::~PausedState() = default;
 
+ServiceWorkerSingleScriptUpdateChecker::FailureInfo::FailureInfo(
+    blink::ServiceWorkerStatusCode status,
+    const std::string& error_message)
+    : status(status), error_message(error_message) {}
+ServiceWorkerSingleScriptUpdateChecker::FailureInfo::~FailureInfo() = default;
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.h b/content/browser/service_worker/service_worker_single_script_update_checker.h
index 49dee2d9..cacf99a 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker.h
+++ b/content/browser/service_worker/service_worker_single_script_update_checker.h
@@ -10,6 +10,7 @@
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
+#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
 
 namespace network {
@@ -39,6 +40,16 @@
     kDifferent,
   };
 
+  // This contains detailed error info of update check when it failed.
+  struct CONTENT_EXPORT FailureInfo {
+    FailureInfo(blink::ServiceWorkerStatusCode status,
+                const std::string& error_message);
+    ~FailureInfo();
+
+    const blink::ServiceWorkerStatusCode status;
+    const std::string error_message;
+  };
+
   // The paused state consists of Mojo endpoints and a cache writer
   // detached/paused in the middle of loading script body and would be used in
   // the left steps of the update process.
@@ -66,9 +77,12 @@
   // It notifies the check result to the callback and the ownership of
   // internal state variables (the cache writer and Mojo endpoints for loading)
   // is transferred to the callback in the PausedState only when the result is
-  // Result::kDifferent. Otherwise it's set to nullptr.
-  using ResultCallback = base::OnceCallback<
-      void(const GURL&, Result, std::unique_ptr<PausedState>)>;
+  // Result::kDifferent. Otherwise it's set to nullptr. FailureInfo is set to a
+  // valid value if the result is Result::kFailed, otherwise it'll be nullptr.
+  using ResultCallback = base::OnceCallback<void(const GURL&,
+                                                 Result,
+                                                 std::unique_ptr<FailureInfo>,
+                                                 std::unique_ptr<PausedState>)>;
 
   // Both |compare_reader| and |copy_reader| should be created from the same
   // resource ID, and this ID should locate where the script specified with
@@ -76,6 +90,7 @@
   ServiceWorkerSingleScriptUpdateChecker(
       const GURL& url,
       bool is_main_script,
+      const GURL& scope,
       bool force_bypass_cache,
       blink::mojom::ServiceWorkerUpdateViaCache update_via_cache,
       base::TimeDelta time_since_last_check,
@@ -105,6 +120,8 @@
   bool network_accessed() const { return network_accessed_; }
 
  private:
+  class WrappedIOBuffer;
+
   void WriteHeaders(scoped_refptr<HttpResponseInfoIOBuffer> info_buffer);
   void OnWriteHeadersComplete(net::Error error);
 
@@ -118,9 +135,18 @@
       scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,
       uint32_t bytes_written,
       net::Error error);
-  void Finish(Result result);
+
+  // Called when the update check for this script failed. It calls Finish().
+  void Fail(blink::ServiceWorkerStatusCode status,
+            const std::string& error_message);
+
+  // Called when the update check for this script succeeded. It calls Finish().
+  void Succeed(Result result);
+  void Finish(Result result, std::unique_ptr<FailureInfo> failure_info);
 
   const GURL script_url_;
+  const bool is_main_script_;
+  const GURL scope_;
   const bool force_bypass_cache_;
   const blink::mojom::ServiceWorkerUpdateViaCache update_via_cache_;
   const base::TimeDelta time_since_last_check_;
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc b/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
index 3dd1a98..0d07db7 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
+++ b/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
@@ -22,6 +22,7 @@
 
 constexpr char kScriptURL[] = "https://example.com/script.js";
 constexpr char kImportedScriptURL[] = "https://example.com/imported-script.js";
+constexpr char kScope[] = "https://example.com/";
 constexpr char kSuccessHeader[] =
     "HTTP/1.1 200 OK\n"
     "Content-Type: text/javascript\n\n";
@@ -32,10 +33,13 @@
     CheckResult(
         const GURL& script_url,
         ServiceWorkerSingleScriptUpdateChecker::Result compare_result,
+        std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::FailureInfo>
+            failure_info,
         std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::PausedState>
             paused_state)
         : url(script_url),
           result(compare_result),
+          failure_info(std::move(failure_info)),
           paused_state(std::move(paused_state)) {}
 
     CheckResult(CheckResult&& ref) = default;
@@ -46,6 +50,8 @@
 
     GURL url;
     ServiceWorkerSingleScriptUpdateChecker::Result result;
+    std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::FailureInfo>
+        failure_info;
     std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::PausedState>
         paused_state;
   };
@@ -74,13 +80,14 @@
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker>
   CreateSingleScriptUpdateCheckerWithoutHttpCache(
       const char* url,
+      const GURL& scope,
       std::unique_ptr<ServiceWorkerResponseReader> compare_reader,
       std::unique_ptr<ServiceWorkerResponseReader> copy_reader,
       std::unique_ptr<ServiceWorkerResponseWriter> writer,
       network::TestURLLoaderFactory* loader_factory,
       base::Optional<CheckResult>* out_check_result) {
     return CreateSingleScriptUpdateChecker(
-        url, true /* is_main_script */, false /* force_bypass_cache */,
+        url, scope, true /* is_main_script */, false /* force_bypass_cache */,
         blink::mojom::ServiceWorkerUpdateViaCache::kNone,
         base::TimeDelta() /* time_since_last_check */,
         std::move(compare_reader), std::move(copy_reader), std::move(writer),
@@ -90,6 +97,7 @@
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker>
   CreateSingleScriptUpdateChecker(
       const char* url,
+      const GURL& scope,
       bool is_main_script,
       bool force_bypass_cache,
       blink::mojom::ServiceWorkerUpdateViaCache update_via_cache,
@@ -101,7 +109,7 @@
       base::Optional<CheckResult>* out_check_result) {
     helper_->SetNetworkFactory(loader_factory);
     return std::make_unique<ServiceWorkerSingleScriptUpdateChecker>(
-        GURL(url), is_main_script, force_bypass_cache, update_via_cache,
+        GURL(url), is_main_script, scope, force_bypass_cache, update_via_cache,
         time_since_last_check,
         helper_->url_loader_factory_getter()->GetNetworkFactory(),
         std::move(compare_reader), std::move(copy_reader), std::move(writer),
@@ -110,23 +118,28 @@
                const GURL& script_url,
                ServiceWorkerSingleScriptUpdateChecker::Result result,
                std::unique_ptr<
+                   ServiceWorkerSingleScriptUpdateChecker::FailureInfo>
+                   failure_info,
+               std::unique_ptr<
                    ServiceWorkerSingleScriptUpdateChecker::PausedState>
                    paused_state) {
               *out_check_result_param =
-                  CheckResult(script_url, result, std::move(paused_state));
+                  CheckResult(script_url, result, std::move(failure_info),
+                              std::move(paused_state));
             },
             out_check_result));
   }
 
   std::unique_ptr<network::TestURLLoaderFactory> CreateLoaderFactoryWithRespone(
       const GURL& url,
-      std::string header,
-      std::string body,
+      const std::string& header,
+      const std::string& body,
       net::Error error) {
     auto loader_factory = std::make_unique<network::TestURLLoaderFactory>();
     network::ResourceResponseHead head;
     head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
         net::HttpUtil::AssembleRawHeaders(header));
+    head.headers->GetMimeType(&head.mime_type);
     network::URLLoaderCompletionStatus status(error);
     status.decoded_body_length = body.size();
     loader_factory->AddResponse(url, head, body, status);
@@ -162,8 +175,9 @@
   base::Optional<CheckResult> check_result;
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
       CreateSingleScriptUpdateCheckerWithoutHttpCache(
-          kScriptURL, std::move(compare_reader), std::move(copy_reader),
-          std::move(writer), loader_factory.get(), &check_result);
+          kScriptURL, GURL(kScope), std::move(compare_reader),
+          std::move(copy_reader), std::move(writer), loader_factory.get(),
+          &check_result);
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(check_result.has_value());
   EXPECT_EQ(check_result.value().result,
@@ -193,8 +207,9 @@
   base::Optional<CheckResult> check_result;
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
       CreateSingleScriptUpdateCheckerWithoutHttpCache(
-          kScriptURL, std::move(compare_reader), std::move(copy_reader),
-          std::move(writer), loader_factory.get(), &check_result);
+          kScriptURL, GURL(kScope), std::move(compare_reader),
+          std::move(copy_reader), std::move(writer), loader_factory.get(),
+          &check_result);
 
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(check_result.has_value());
@@ -226,8 +241,9 @@
   base::Optional<CheckResult> check_result;
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
       CreateSingleScriptUpdateCheckerWithoutHttpCache(
-          kScriptURL, std::move(compare_reader), std::move(copy_reader),
-          std::move(writer), loader_factory.get(), &check_result);
+          kScriptURL, GURL(kScope), std::move(compare_reader),
+          std::move(copy_reader), std::move(writer), loader_factory.get(),
+          &check_result);
 
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(check_result.has_value());
@@ -258,8 +274,10 @@
   base::Optional<CheckResult> check_result;
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
       CreateSingleScriptUpdateCheckerWithoutHttpCache(
-          kScriptURL, std::move(compare_reader), std::move(copy_reader),
-          std::move(writer), loader_factory.get(), &check_result);
+          kScriptURL, GURL(kScope), std::move(compare_reader),
+          std::move(copy_reader), std::move(writer), loader_factory.get(),
+          &check_result);
+
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(check_result.has_value());
   EXPECT_EQ(check_result.value().result,
@@ -293,8 +311,10 @@
   base::Optional<CheckResult> check_result;
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
       CreateSingleScriptUpdateCheckerWithoutHttpCache(
-          kScriptURL, std::move(compare_reader), std::move(copy_reader),
-          std::move(writer), loader_factory.get(), &check_result);
+          kScriptURL, GURL(kScope), std::move(compare_reader),
+          std::move(copy_reader), std::move(writer), loader_factory.get(),
+          &check_result);
+
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(check_result.has_value());
   EXPECT_EQ(check_result.value().result,
@@ -324,8 +344,9 @@
   base::Optional<CheckResult> check_result;
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
       CreateSingleScriptUpdateCheckerWithoutHttpCache(
-          kScriptURL, std::move(compare_reader), std::move(copy_reader),
-          std::move(writer), loader_factory.get(), &check_result);
+          kScriptURL, GURL(kScope), std::move(compare_reader),
+          std::move(copy_reader), std::move(writer), loader_factory.get(),
+          &check_result);
 
   // Update check stops in WriteHeader() due to the asynchronous read of the
   // |compare_reader|.
@@ -358,7 +379,8 @@
   // Load the main script. Should not validate the cache.
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
       CreateSingleScriptUpdateChecker(
-          kScriptURL, true /* is_main_script */, false /* force_bypass_cache */,
+          kScriptURL, GURL(kScope), true /* is_main_script */,
+          false /* force_bypass_cache */,
           blink::mojom::ServiceWorkerUpdateViaCache::kAll, base::TimeDelta(),
           std::make_unique<MockServiceWorkerResponseReader>(),
           std::make_unique<MockServiceWorkerResponseReader>(),
@@ -371,7 +393,7 @@
 
   // Load imported script. Should not validate the cache.
   checker = CreateSingleScriptUpdateChecker(
-      kImportedScriptURL, false /* is_main_script */,
+      kImportedScriptURL, GURL(kScope), false /* is_main_script */,
       false /* force_bypass_cache */,
       blink::mojom::ServiceWorkerUpdateViaCache::kAll, base::TimeDelta(),
       std::make_unique<MockServiceWorkerResponseReader>(),
@@ -391,7 +413,8 @@
   // Load the main script. Should validate the cache.
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
       CreateSingleScriptUpdateChecker(
-          kScriptURL, true /* is_main_script */, false /* force_bypass_cache */,
+          kScriptURL, GURL(kScope), true /* is_main_script */,
+          false /* force_bypass_cache */,
           blink::mojom::ServiceWorkerUpdateViaCache::kNone, base::TimeDelta(),
           std::make_unique<MockServiceWorkerResponseReader>(),
           std::make_unique<MockServiceWorkerResponseReader>(),
@@ -404,7 +427,7 @@
 
   // Load imported script. Should validate the cache.
   checker = CreateSingleScriptUpdateChecker(
-      kImportedScriptURL, false /* is_main_script */,
+      kImportedScriptURL, GURL(kScope), false /* is_main_script */,
       false /* force_bypass_cache */,
       blink::mojom::ServiceWorkerUpdateViaCache::kNone, base::TimeDelta(),
       std::make_unique<MockServiceWorkerResponseReader>(),
@@ -424,7 +447,8 @@
   // Load main script. Should validate the cache.
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
       CreateSingleScriptUpdateChecker(
-          kScriptURL, true /* is_main_script */, false /* force_bypass_cache */,
+          kScriptURL, GURL(kScope), true /* is_main_script */,
+          false /* force_bypass_cache */,
           blink::mojom::ServiceWorkerUpdateViaCache::kImports,
           base::TimeDelta(),
           std::make_unique<MockServiceWorkerResponseReader>(),
@@ -438,7 +462,7 @@
 
   // Load imported script. Should not validate the cache.
   checker = CreateSingleScriptUpdateChecker(
-      kImportedScriptURL, false /* is_main_script */,
+      kImportedScriptURL, GURL(kScope), false /* is_main_script */,
       false /* force_bypass_cache */,
       blink::mojom::ServiceWorkerUpdateViaCache::kImports, base::TimeDelta(),
       std::make_unique<MockServiceWorkerResponseReader>(),
@@ -459,7 +483,8 @@
   // Load main script. Should validate the cache.
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
       CreateSingleScriptUpdateChecker(
-          kScriptURL, true /* is_main_script */, true /* force_bypass_cache */,
+          kScriptURL, GURL(kScope), true /* is_main_script */,
+          true /* force_bypass_cache */,
           blink::mojom::ServiceWorkerUpdateViaCache::kAll, base::TimeDelta(),
           std::make_unique<MockServiceWorkerResponseReader>(),
           std::make_unique<MockServiceWorkerResponseReader>(),
@@ -472,7 +497,7 @@
 
   // Load imported script. Should validate the cache.
   checker = CreateSingleScriptUpdateChecker(
-      kImportedScriptURL, false /* is_main_script */,
+      kImportedScriptURL, GURL(kScope), false /* is_main_script */,
       true /* force_bypass_cache */,
       blink::mojom::ServiceWorkerUpdateViaCache::kAll, base::TimeDelta(),
       std::make_unique<MockServiceWorkerResponseReader>(),
@@ -492,7 +517,8 @@
   // Load main script. Should validate the cache.
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
       CreateSingleScriptUpdateChecker(
-          kScriptURL, true /* is_main_script */, false /* force_bypass_cache */,
+          kScriptURL, GURL(kScope), true /* is_main_script */,
+          false /* force_bypass_cache */,
           blink::mojom::ServiceWorkerUpdateViaCache::kAll,
           base::TimeDelta::FromDays(1) + base::TimeDelta::FromHours(1),
           std::make_unique<MockServiceWorkerResponseReader>(),
@@ -506,7 +532,7 @@
 
   // Load imported script. Should validate the cache.
   checker = CreateSingleScriptUpdateChecker(
-      kImportedScriptURL, false /* is_main_script */,
+      kImportedScriptURL, GURL(kScope), false /* is_main_script */,
       false /* force_bypass_cache */,
       blink::mojom::ServiceWorkerUpdateViaCache::kAll,
       base::TimeDelta::FromDays(1) + base::TimeDelta::FromHours(1),
@@ -519,5 +545,155 @@
   EXPECT_TRUE(request->load_flags & net::LOAD_VALIDATE_CACHE);
 }
 
+// Tests MIME type header checking.
+TEST_F(ServiceWorkerSingleScriptUpdateCheckerTest, MimeTypeError) {
+  // Response body from the network.
+  const std::string kBodyFromNet = "abcdef";
+
+  // It should report error for no/bad MIME types.
+  const char* kNoMimeHeader = "HTTP/1.1 200 OK\n\n";
+  const char* kBadMimeHeader =
+      "HTTP/1.1 200 OK\n"
+      "Content-Type: text/css\n\n";
+  const std::string headers[] = {kNoMimeHeader, kBadMimeHeader};
+
+  for (const std::string& header : headers) {
+    std::unique_ptr<network::TestURLLoaderFactory> loader_factory =
+        CreateLoaderFactoryWithRespone(GURL(kScriptURL), header, kBodyFromNet,
+                                       net::OK);
+
+    auto compare_reader = std::make_unique<MockServiceWorkerResponseReader>();
+    auto copy_reader = std::make_unique<MockServiceWorkerResponseReader>();
+    auto writer = std::make_unique<MockServiceWorkerResponseWriter>();
+
+    base::Optional<CheckResult> check_result;
+    std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
+        CreateSingleScriptUpdateChecker(
+            kScriptURL, GURL(kScope), true /* is_main_script */,
+            false /* force_bypass_cache */,
+            blink::mojom::ServiceWorkerUpdateViaCache::kNone, base::TimeDelta(),
+            std::move(compare_reader), std::move(copy_reader),
+            std::move(writer), loader_factory.get(), &check_result);
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_TRUE(check_result.has_value());
+    EXPECT_EQ(check_result.value().result,
+              ServiceWorkerSingleScriptUpdateChecker::Result::kFailed);
+    EXPECT_EQ(check_result.value().failure_info->status,
+              blink::ServiceWorkerStatusCode::kErrorSecurity);
+  }
+}
+
+// Tests path restriction check error for main script.
+// |kOutScope| is not under the default scope ("/in-scope/") and the
+// Service-Worker-Allowed header is not specified. The check should fail.
+TEST_F(ServiceWorkerSingleScriptUpdateCheckerTest, PathRestrictionError) {
+  // Response body from the network.
+  const std::string kBodyFromNet = "abcdef";
+  const char kMainScriptURL[] = "https://example.com/in-scope/worker.js";
+  const char kOutScope[] = "https://example.com/out-scope/";
+  const char kHeader[] =
+      "HTTP/1.1 200 OK\n"
+      "Content-Type: text/javascript\n\n";
+  std::unique_ptr<network::TestURLLoaderFactory> loader_factory =
+      CreateLoaderFactoryWithRespone(GURL(kMainScriptURL), kHeader,
+                                     kBodyFromNet, net::OK);
+
+  auto compare_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto copy_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto writer = std::make_unique<MockServiceWorkerResponseWriter>();
+
+  base::Optional<CheckResult> check_result;
+  std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
+      CreateSingleScriptUpdateChecker(
+          kMainScriptURL, GURL(kOutScope), true /* is_main_script */,
+          false /* force_bypass_cache */,
+          blink::mojom::ServiceWorkerUpdateViaCache::kNone, base::TimeDelta(),
+          std::move(compare_reader), std::move(copy_reader), std::move(writer),
+          loader_factory.get(), &check_result);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(check_result.has_value());
+  EXPECT_EQ(check_result.value().result,
+            ServiceWorkerSingleScriptUpdateChecker::Result::kFailed);
+  EXPECT_EQ(check_result.value().failure_info->status,
+            blink::ServiceWorkerStatusCode::kErrorSecurity);
+}
+
+// Tests path restriction check success for main script.
+// |kOutScope| is not under the default scope ("/in-scope/") but the
+// Service-Worker-Allowed header allows it. The check should pass.
+TEST_F(ServiceWorkerSingleScriptUpdateCheckerTest, PathRestrictionPass) {
+  // Response body from the network.
+  const std::string body_from_net("abcdef");
+  const char kMainScriptURL[] = "https://example.com/in-scope/worker.js";
+  const char kOutScope[] = "https://example.com/out-scope/";
+  const char kHeader[] =
+      "HTTP/1.1 200 OK\n"
+      "Content-Type: text/javascript\n"
+      "Service-Worker-Allowed: /out-scope/\n\n";
+
+  // Stored data for |kMainScriptURL|.
+  const std::vector<std::string> body_from_storage{body_from_net};
+
+  std::unique_ptr<network::TestURLLoaderFactory> loader_factory =
+      CreateLoaderFactoryWithRespone(GURL(kMainScriptURL), kHeader,
+                                     body_from_net, net::OK);
+
+  auto compare_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto copy_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto writer = std::make_unique<MockServiceWorkerResponseWriter>();
+  MockServiceWorkerResponseReader* compare_reader_rawptr = compare_reader.get();
+  compare_reader->ExpectReadOk(body_from_storage, TotalBytes(body_from_storage),
+                               false /* async */);
+
+  base::Optional<CheckResult> check_result;
+  std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
+      CreateSingleScriptUpdateChecker(
+          kMainScriptURL, GURL(kOutScope), true /* is_main_script */,
+          false /* force_bypass_cache */,
+          blink::mojom::ServiceWorkerUpdateViaCache::kNone, base::TimeDelta(),
+          std::move(compare_reader), std::move(copy_reader), std::move(writer),
+          loader_factory.get(), &check_result);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(check_result.has_value());
+  EXPECT_EQ(check_result.value().result,
+            ServiceWorkerSingleScriptUpdateChecker::Result::kIdentical);
+  EXPECT_EQ(check_result.value().url, kMainScriptURL);
+  EXPECT_EQ(check_result.value().failure_info, nullptr);
+  EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
+}
+
+// Tests network error is reported.
+TEST_F(ServiceWorkerSingleScriptUpdateCheckerTest, NetworkError) {
+  // Response body from the network.
+  const std::string kBodyFromNet = "abcdef";
+  const char kFailHeader[] = "HTTP/1.1 404 Not Found\n\n";
+  std::unique_ptr<network::TestURLLoaderFactory> loader_factory =
+      CreateLoaderFactoryWithRespone(GURL(kScriptURL), kFailHeader,
+                                     kBodyFromNet, net::OK);
+
+  auto compare_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto copy_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto writer = std::make_unique<MockServiceWorkerResponseWriter>();
+
+  base::Optional<CheckResult> check_result;
+  std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
+      CreateSingleScriptUpdateChecker(
+          kScriptURL, GURL(kScope), true /* is_main_script */,
+          false /* force_bypass_cache */,
+          blink::mojom::ServiceWorkerUpdateViaCache::kNone, base::TimeDelta(),
+          std::move(compare_reader), std::move(copy_reader), std::move(writer),
+          loader_factory.get(), &check_result);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(check_result.has_value());
+  EXPECT_EQ(check_result.value().result,
+            ServiceWorkerSingleScriptUpdateChecker::Result::kFailed);
+  EXPECT_EQ(check_result.value().failure_info->status,
+            blink::ServiceWorkerStatusCode::kErrorNetwork);
+}
+
 }  // namespace
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_update_checker.cc b/content/browser/service_worker/service_worker_update_checker.cc
index c28bc03..536c44e 100644
--- a/content/browser/service_worker/service_worker_update_checker.cc
+++ b/content/browser/service_worker/service_worker_update_checker.cc
@@ -42,8 +42,23 @@
     int64_t old_resource_id,
     const GURL& script_url,
     ServiceWorkerSingleScriptUpdateChecker::Result result,
+    std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::FailureInfo>
+        failure_info,
     std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::PausedState>
         paused_state) {
+  bool is_main_script = script_url == main_script_url_;
+  // We only cares about the failures on the main script because an imported
+  // script might not exist anymore and fail to be loaded because it's not
+  // imported in a new script.
+  // See also https://github.com/w3c/ServiceWorker/issues/1374 for more details.
+  if (is_main_script &&
+      result == ServiceWorkerSingleScriptUpdateChecker::Result::kFailed) {
+    std::move(callback_).Run(
+        ServiceWorkerSingleScriptUpdateChecker::Result::kFailed,
+        std::move(failure_info));
+    return;
+  }
+
   script_check_results_[script_url] =
       ComparedScriptInfo(old_resource_id, result, std::move(paused_state));
   if (running_checker_->network_accessed())
@@ -55,14 +70,18 @@
     // Found an updated script. Stop the comparison of scripts here and
     // return to ServiceWorkerRegisterJob to continue the update.
     // Note that running |callback_| will delete |this|.
-    std::move(callback_).Run(true);
+    std::move(callback_).Run(
+        ServiceWorkerSingleScriptUpdateChecker::Result::kDifferent,
+        nullptr /* failure_info */);
     return;
   }
 
   if (next_script_index_to_compare_ >= scripts_to_compare_.size()) {
     // None of scripts had any updates.
     // Running |callback_| will delete |this|.
-    std::move(callback_).Run(false);
+    std::move(callback_).Run(
+        ServiceWorkerSingleScriptUpdateChecker::Result::kIdentical,
+        nullptr /* failure_info */);
     return;
   }
 
@@ -73,7 +92,9 @@
     if (next_script_index_to_compare_ >= scripts_to_compare_.size()) {
       // None of scripts had any updates.
       // Running |callback_| will delete |this|.
-      std::move(callback_).Run(false);
+      std::move(callback_).Run(
+          ServiceWorkerSingleScriptUpdateChecker::Result::kIdentical,
+          nullptr /* failure_info */);
       return;
     }
   }
@@ -105,9 +126,9 @@
 
   auto writer = storage->CreateResponseWriter(storage->NewResourceId());
   running_checker_ = std::make_unique<ServiceWorkerSingleScriptUpdateChecker>(
-      url, is_main_script, force_bypass_cache_, update_via_cache_,
-      time_since_last_check_, loader_factory_, std::move(compare_reader),
-      std::move(copy_reader), std::move(writer),
+      url, is_main_script, version_to_update_->scope(), force_bypass_cache_,
+      update_via_cache_, time_since_last_check_, loader_factory_,
+      std::move(compare_reader), std::move(copy_reader), std::move(writer),
       base::BindOnce(&ServiceWorkerUpdateChecker::OnOneUpdateCheckFinished,
                      weak_factory_.GetWeakPtr(), resource_id));
 }
diff --git a/content/browser/service_worker/service_worker_update_checker.h b/content/browser/service_worker/service_worker_update_checker.h
index 3b60357bd..6dba085e 100644
--- a/content/browser/service_worker/service_worker_update_checker.h
+++ b/content/browser/service_worker/service_worker_update_checker.h
@@ -44,7 +44,18 @@
         paused_state;
   };
 
-  using UpdateStatusCallback = base::OnceCallback<void(bool)>;
+  // This is to notify the update check result. Value of |result| can be:
+  // 1. ServiceWorkerSingleScriptUpdateChecker::Result::kIdentical
+  //    All the scripts are identical with existing version, no need to update.
+  //    |failure_info| is nullptr.
+  // 2. ServiceWorkerSingleScriptUpdateChecker::Result::kDifferent
+  //    Some script changed, update is needed. |failure_info| is nullptr.
+  // 3. ServiceWorkerSingleScriptUpdateChecker::Result::kFailed
+  //    The update check failed, detailed error info is in |failure_info|.
+  using UpdateStatusCallback = base::OnceCallback<void(
+      ServiceWorkerSingleScriptUpdateChecker::Result result,
+      std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::FailureInfo>
+          failure_info)>;
 
   ServiceWorkerUpdateChecker(
       std::vector<ServiceWorkerDatabase::ResourceRecord> scripts_to_compare,
@@ -57,9 +68,7 @@
       base::TimeDelta time_since_last_check);
   ~ServiceWorkerUpdateChecker();
 
-  // |callback| is always triggered when Start() finishes. If the scripts are
-  // found to have any changes, the argument of |callback| is true and otherwise
-  // false.
+  // |callback| is always triggered when Start() finishes.
   void Start(UpdateStatusCallback callback);
 
   // This transfers the ownership of the check result to the caller. It only
@@ -70,6 +79,8 @@
       int64_t old_resource_id,
       const GURL& script_url,
       ServiceWorkerSingleScriptUpdateChecker::Result result,
+      std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::FailureInfo>
+          failure_info,
       std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::PausedState>
           paused_state);
 
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index 95f9ad2..0c4dfe1fee 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -2738,8 +2738,9 @@
 
 // This test tests that browser process hittesting ignores frames with
 // pointer-events: none.
+// TODO(https://crbug.com/968970): Flaky failures on all bots.
 IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
-                       SurfaceHitTestPointerEventsNoneChanged) {
+                       DISABLED_SurfaceHitTestPointerEventsNoneChanged) {
   // In /2 hit testing, OOPIFs with pointer-events: none are ignored and no hit
   // test data is submitted. To make sure we wait enough time until child frame
   // fully loaded, we add a 1x1 pixel OOPIF for the test to track the process of
diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc
index 1ce0d53ba..830ef3b1 100644
--- a/content/browser/storage_partition_impl_map.cc
+++ b/content/browser/storage_partition_impl_map.cc
@@ -35,10 +35,6 @@
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/browser/resource_context_impl.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/browser/streams/stream.h"
-#include "content/browser/streams/stream_context.h"
-#include "content/browser/streams/stream_registry.h"
-#include "content/browser/streams/stream_url_request_job.h"
 #include "content/browser/webui/url_data_manager_backend.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/browser/browser_context.h"
@@ -65,41 +61,6 @@
 
 namespace {
 
-// A derivative that knows about Streams too.
-class BlobProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
- public:
-  BlobProtocolHandler(ChromeBlobStorageContext* blob_storage_context,
-                      StreamContext* stream_context)
-      : blob_storage_context_(blob_storage_context),
-        stream_context_(stream_context) {}
-
-  ~BlobProtocolHandler() override {}
-
-  net::URLRequestJob* MaybeCreateJob(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
-    scoped_refptr<Stream> stream =
-        stream_context_->registry()->GetStream(request->url());
-    if (stream.get())
-      return new StreamURLRequestJob(request, network_delegate, stream);
-
-    if (!blob_protocol_handler_) {
-      // Construction is deferred because 'this' is constructed on
-      // the main thread but we want blob_protocol_handler_ constructed
-      // on the IO thread.
-      blob_protocol_handler_.reset(
-          new storage::BlobProtocolHandler(blob_storage_context_->context()));
-    }
-    return blob_protocol_handler_->MaybeCreateJob(request, network_delegate);
-  }
-
- private:
-  const scoped_refptr<ChromeBlobStorageContext> blob_storage_context_;
-  const scoped_refptr<StreamContext> stream_context_;
-  mutable std::unique_ptr<storage::BlobProtocolHandler> blob_protocol_handler_;
-  DISALLOW_COPY_AND_ASSIGN(BlobProtocolHandler);
-};
-
 // These constants are used to create the directory structure under the profile
 // where renderers with a non-default storage partition keep their persistent
 // state. This will contain a set of directories that partially mirror the
@@ -401,10 +362,10 @@
 
   ChromeBlobStorageContext* blob_storage_context =
       ChromeBlobStorageContext::GetFor(browser_context_);
-  StreamContext* stream_context = StreamContext::GetFor(browser_context_);
   ProtocolHandlerMap protocol_handlers;
-  protocol_handlers[url::kBlobScheme] = std::make_unique<BlobProtocolHandler>(
-      blob_storage_context, stream_context);
+  protocol_handlers[url::kBlobScheme] =
+      std::make_unique<storage::BlobProtocolHandler>(
+          blob_storage_context->context());
   protocol_handlers[url::kFileSystemScheme] = CreateFileSystemProtocolHandler(
       partition_domain, partition->GetFileSystemContext());
   for (const auto& scheme : URLDataManagerBackend::GetWebUISchemes()) {
diff --git a/content/browser/streams/OWNERS b/content/browser/streams/OWNERS
deleted file mode 100644
index a114309..0000000
--- a/content/browser/streams/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-ricea@chromium.org
-yhirano@chromium.org
-
-# TEAM: blink-network-dev@chromium.org
-# COMPONENT: Blink>Network>StreamsAPI
diff --git a/content/browser/streams/stream.cc b/content/browser/streams/stream.cc
deleted file mode 100644
index 1ac6ef6..0000000
--- a/content/browser/streams/stream.cc
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/streams/stream.h"
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/values.h"
-#include "content/browser/streams/stream_handle_impl.h"
-#include "content/browser/streams/stream_metadata.h"
-#include "content/browser/streams/stream_read_observer.h"
-#include "content/browser/streams/stream_registry.h"
-#include "content/browser/streams/stream_write_observer.h"
-#include "net/base/io_buffer.h"
-#include "net/http/http_response_headers.h"
-
-namespace {
-// Start throttling the connection at about 1MB.
-const size_t kDeferSizeThreshold = 40 * 32768;
-}  // namespace
-
-namespace content {
-
-Stream::Stream(StreamRegistry* registry,
-               StreamWriteObserver* write_observer,
-               const GURL& url)
-    : can_add_data_(true),
-      url_(url),
-      data_length_(0),
-      data_bytes_read_(0),
-      last_total_buffered_bytes_(0),
-      registry_(registry),
-      read_observer_(nullptr),
-      write_observer_(write_observer),
-      stream_handle_(nullptr),
-      weak_ptr_factory_(this) {
-  CreateByteStream(base::ThreadTaskRunnerHandle::Get(),
-                   base::ThreadTaskRunnerHandle::Get(), kDeferSizeThreshold,
-                   &writer_, &reader_);
-
-  // Setup callback for writing.
-  writer_->RegisterCallback(base::BindRepeating(
-      &Stream::OnSpaceAvailable, weak_ptr_factory_.GetWeakPtr()));
-  reader_->RegisterCallback(base::BindRepeating(
-      &Stream::OnDataAvailable, weak_ptr_factory_.GetWeakPtr()));
-
-  registry_->RegisterStream(this);
-}
-
-Stream::~Stream() {
-}
-
-bool Stream::SetReadObserver(StreamReadObserver* observer) {
-  if (read_observer_)
-    return false;
-  read_observer_ = observer;
-  return true;
-}
-
-void Stream::RemoveReadObserver(StreamReadObserver* observer) {
-  DCHECK_EQ(observer, read_observer_);
-  read_observer_ = nullptr;
-}
-
-void Stream::RemoveWriteObserver(StreamWriteObserver* observer) {
-  DCHECK_EQ(observer, write_observer_);
-  write_observer_ = nullptr;
-}
-
-void Stream::Abort() {
-  // Clear all buffer. It's safe to clear reader_ here since the same thread
-  // is used for both input and output operation.
-  writer_.reset();
-  reader_.reset();
-  ClearBuffer();
-  can_add_data_ = false;
-  registry_->UnregisterStream(url());
-  // Notify the observer that something happens. Read will return
-  // STREAM_ABORTED.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&Stream::OnDataAvailable, weak_ptr_factory_.GetWeakPtr()));
-}
-
-void Stream::OnResponseStarted(const net::HttpResponseInfo& response_info) {
-  DCHECK(!metadata_);
-  metadata_ = std::make_unique<StreamMetadata>(response_info);
-}
-
-void Stream::UpdateNetworkStats(int64_t raw_body_bytes, int64_t total_bytes) {
-  metadata_->set_raw_body_bytes(raw_body_bytes);
-  metadata_->set_total_received_bytes(total_bytes);
-}
-
-void Stream::AddData(scoped_refptr<net::IOBuffer> buffer, size_t size) {
-  if (!writer_.get())
-    return;
-
-  size_t current_buffered_bytes = writer_->GetTotalBufferedBytes();
-  if (!registry_->UpdateMemoryUsage(url(), current_buffered_bytes, size)) {
-    Abort();
-    return;
-  }
-
-  // Now it's guaranteed that this doesn't overflow. This must be done before
-  // Write() since GetTotalBufferedBytes() may return different value after
-  // Write() call, so if we use the new value, information in this instance and
-  // one in |registry_| become inconsistent.
-  last_total_buffered_bytes_ = current_buffered_bytes + size;
-
-  can_add_data_ = writer_->Write(buffer, size);
-}
-
-void Stream::Flush() {
-  if (!writer_.get())
-    return;
-  writer_->Flush();
-}
-
-void Stream::Finalize(int status) {
-  if (!writer_.get())
-    return;
-
-  writer_->Close(status);
-  writer_.reset();
-
-  // Continue asynchronously.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&Stream::OnDataAvailable, weak_ptr_factory_.GetWeakPtr()));
-}
-
-Stream::StreamState Stream::ReadRawData(net::IOBuffer* buf,
-                                        int buf_size,
-                                        int* bytes_read) {
-  DCHECK(buf);
-  DCHECK(bytes_read);
-
-  *bytes_read = 0;
-  if (!data_.get()) {
-    DCHECK(!data_length_);
-    DCHECK(!data_bytes_read_);
-
-    if (!reader_.get())
-      return STREAM_ABORTED;
-
-    ByteStreamReader::StreamState state = reader_->Read(&data_, &data_length_);
-    switch (state) {
-      case ByteStreamReader::STREAM_HAS_DATA:
-        break;
-      case ByteStreamReader::STREAM_COMPLETE:
-        registry_->UnregisterStream(url());
-        return STREAM_COMPLETE;
-      case ByteStreamReader::STREAM_EMPTY:
-        return STREAM_EMPTY;
-    }
-  }
-
-  const size_t remaining_bytes = data_length_ - data_bytes_read_;
-  size_t to_read =
-      static_cast<size_t>(buf_size) < remaining_bytes ?
-      buf_size : remaining_bytes;
-  memcpy(buf->data(), data_->data() + data_bytes_read_, to_read);
-  data_bytes_read_ += to_read;
-  if (data_bytes_read_ >= data_length_)
-    ClearBuffer();
-
-  *bytes_read = to_read;
-  return STREAM_HAS_DATA;
-}
-
-std::unique_ptr<StreamHandle> Stream::CreateHandle() {
-  CHECK(!stream_handle_);
-  stream_handle_ = new StreamHandleImpl(weak_ptr_factory_.GetWeakPtr());
-  return std::unique_ptr<StreamHandle>(stream_handle_);
-}
-
-void Stream::CloseHandle() {
-  // Prevent deletion until this function ends.
-  scoped_refptr<Stream> ref(this);
-
-  CHECK(stream_handle_);
-  stream_handle_ = nullptr;
-  registry_->UnregisterStream(url());
-  if (write_observer_)
-    write_observer_->OnClose(this);
-}
-
-int Stream::GetStatus() {
-  return reader_->GetStatus();
-}
-
-void Stream::OnSpaceAvailable() {
-  can_add_data_ = true;
-  if (write_observer_)
-    write_observer_->OnSpaceAvailable(this);
-}
-
-void Stream::OnDataAvailable() {
-  if (read_observer_)
-    read_observer_->OnDataAvailable(this);
-}
-
-void Stream::ClearBuffer() {
-  data_ = nullptr;
-  data_length_ = 0;
-  data_bytes_read_ = 0;
-}
-
-}  // namespace content
diff --git a/content/browser/streams/stream.h b/content/browser/streams/stream.h
deleted file mode 100644
index 1c6309e..0000000
--- a/content/browser/streams/stream.h
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_STREAMS_STREAM_H_
-#define CONTENT_BROWSER_STREAMS_STREAM_H_
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "content/browser/byte_stream.h"
-#include "content/common/content_export.h"
-#include "url/gurl.h"
-
-namespace net {
-class HttpResponseInfo;
-class IOBuffer;
-}
-
-namespace content {
-
-class StreamHandle;
-class StreamHandleImpl;
-class StreamMetadata;
-class StreamReadObserver;
-class StreamRegistry;
-class StreamWriteObserver;
-
-// A stream that sends data from an arbitrary source to an internal URL
-// that can be read by an internal consumer.  It will continue to pull from the
-// original URL as long as there is data available.  It can be read from
-// multiple clients, but only one can be reading at a time. This allows a
-// reader to consume part of the stream, then pass it along to another client
-// to continue processing the stream.
-class CONTENT_EXPORT Stream : public base::RefCountedThreadSafe<Stream> {
- public:
-  enum StreamState {
-    STREAM_HAS_DATA,
-    STREAM_COMPLETE,
-    STREAM_EMPTY,
-    STREAM_ABORTED,
-  };
-
-  // Creates a stream.
-  //
-  // Security origin of Streams is checked in Blink (See BlobRegistry,
-  // BlobURL and SecurityOrigin to understand how it works). There's no security
-  // origin check in Chromium side for now.
-  Stream(StreamRegistry* registry,
-         StreamWriteObserver* write_observer,
-         const GURL& url);
-
-  // Sets the reader of this stream. Returns true on success, or false if there
-  // is already a reader.
-  bool SetReadObserver(StreamReadObserver* observer);
-
-  // Removes the read observer.  |observer| must be the current observer.
-  void RemoveReadObserver(StreamReadObserver* observer);
-
-  // Removes the write observer.  |observer| must be the current observer.
-  void RemoveWriteObserver(StreamWriteObserver* observer);
-
-  // Stops accepting new data, clears all buffer, unregisters this stream from
-  // |registry_| and make coming ReadRawData() calls return STREAM_ABORTED.
-  void Abort();
-
-  // Passes HTTP response information associated with the response body
-  // transferred through this.
-  void OnResponseStarted(const net::HttpResponseInfo& response_info);
-
-  // Updates actual counts of bytes transferred by the network.
-  void UpdateNetworkStats(int64_t raw_body_bytes, int64_t total_bytes);
-
-  // Adds the data in |buffer| to the stream.  Takes ownership of |buffer|.
-  void AddData(scoped_refptr<net::IOBuffer> buffer, size_t size);
-
-  // Flushes contents buffered in the stream to the corresponding reader.
-  void Flush();
-
-  // Notifies this stream that it will not be receiving any more data.
-  void Finalize(int status);
-
-  // Reads a maximum of |buf_size| from the stream into |buf|.  Sets
-  // |*bytes_read| to the number of bytes actually read.
-  // Returns STREAM_HAS_DATA if data was read, STREAM_EMPTY if no data was read,
-  // and STREAM_COMPLETE if the stream is finalized and all data has been read.
-  StreamState ReadRawData(net::IOBuffer* buf, int buf_size, int* bytes_read);
-
-  std::unique_ptr<StreamHandle> CreateHandle();
-  void CloseHandle();
-
-  // Returns the status of the stream. This is either an error code that
-  // occurred while reading, or the status that was set in Finalize above.
-  int GetStatus();
-
-  // Indicates whether there is space in the buffer to add more data.
-  bool can_add_data() const { return can_add_data_; }
-
-  const GURL& url() const { return url_; }
-
-  // For StreamRegistry to remember the last memory usage reported to it.
-  size_t last_total_buffered_bytes() const {
-    return last_total_buffered_bytes_;
-  }
-
-  StreamMetadata* metadata() const { return metadata_.get(); }
-
- private:
-  friend class base::RefCountedThreadSafe<Stream>;
-
-  virtual ~Stream();
-
-  void OnSpaceAvailable();
-  void OnDataAvailable();
-
-  // Clears |data_| and related variables.
-  void ClearBuffer();
-
-  bool can_add_data_;
-
-  const GURL url_;
-
-  // Buffer for storing data read from |reader_| but not yet read out from this
-  // Stream by ReadRawData() method.
-  scoped_refptr<net::IOBuffer> data_;
-  // Number of bytes read from |reader_| into |data_| including bytes already
-  // read out.
-  size_t data_length_;
-  // Number of bytes in |data_| that are already read out.
-  size_t data_bytes_read_;
-
-  // Last value returned by writer_->TotalBufferedBytes() in AddData(). Stored
-  // in order to check memory usage.
-  size_t last_total_buffered_bytes_;
-
-  std::unique_ptr<ByteStreamWriter> writer_;
-  std::unique_ptr<ByteStreamReader> reader_;
-
-  StreamRegistry* registry_;
-  StreamReadObserver* read_observer_;
-  StreamWriteObserver* write_observer_;
-
-  StreamHandleImpl* stream_handle_;
-  std::unique_ptr<StreamMetadata> metadata_;
-
-  base::WeakPtrFactory<Stream> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(Stream);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_STREAMS_STREAM_H_
diff --git a/content/browser/streams/stream_context.cc b/content/browser/streams/stream_context.cc
deleted file mode 100644
index 9ed67fe1..0000000
--- a/content/browser/streams/stream_context.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/streams/stream_context.h"
-
-#include "base/bind.h"
-#include "base/task/post_task.h"
-#include "content/browser/streams/stream_registry.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-
-using base::UserDataAdapter;
-
-namespace {
-
-const char kStreamContextKeyName[] = "content_stream_context";
-
-}  // namespace
-
-namespace content {
-
-StreamContext::StreamContext() {}
-
-StreamContext* StreamContext::GetFor(BrowserContext* context) {
-  if (!context->GetUserData(kStreamContextKeyName)) {
-    scoped_refptr<StreamContext> stream = new StreamContext();
-    context->SetUserData(
-        kStreamContextKeyName,
-        std::make_unique<UserDataAdapter<StreamContext>>(stream.get()));
-    // Check first to avoid memory leak in unittests.
-    if (BrowserThread::IsThreadInitialized(BrowserThread::IO)) {
-      base::PostTaskWithTraits(
-          FROM_HERE, {BrowserThread::IO},
-          base::BindOnce(&StreamContext::InitializeOnIOThread, stream));
-    }
-  }
-
-  return UserDataAdapter<StreamContext>::Get(context, kStreamContextKeyName);
-}
-
-void StreamContext::InitializeOnIOThread() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  registry_.reset(new StreamRegistry());
-}
-
-StreamContext::~StreamContext() {}
-
-void StreamContext::DeleteOnCorrectThread() const {
-  // In many tests, there isn't a valid IO thread.  In that case, just delete on
-  // the current thread.
-  // TODO(zork): Remove this custom deleter, and fix the leaks in all the
-  // tests.
-  if (BrowserThread::IsThreadInitialized(BrowserThread::IO) &&
-      !BrowserThread::CurrentlyOn(BrowserThread::IO)) {
-    BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, this);
-    return;
-  }
-  delete this;
-}
-
-}  // namespace content
diff --git a/content/browser/streams/stream_context.h b/content/browser/streams/stream_context.h
deleted file mode 100644
index 075ae3e..0000000
--- a/content/browser/streams/stream_context.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_STREAMS_STREAM_CONTEXT_H_
-#define CONTENT_BROWSER_STREAMS_STREAM_CONTEXT_H_
-
-#include <memory>
-
-#include "base/memory/ref_counted.h"
-#include "base/sequenced_task_runner_helpers.h"
-#include "content/common/content_export.h"
-
-namespace content {
-class BrowserContext;
-class StreamRegistry;
-struct StreamContextDeleter;
-
-// A context class that keeps track of StreamRegistry used by the chrome.
-// There is an instance associated with each BrowserContext. There could be
-// multiple URLRequestContexts in the same browser context that refers to the
-// same instance.
-//
-// All methods, except the ctor, are expected to be called on
-// the IO thread (unless specifically called out in doc comments).
-class StreamContext
-    : public base::RefCountedThreadSafe<StreamContext,
-                                        StreamContextDeleter> {
- public:
-  StreamContext();
-
-  CONTENT_EXPORT static StreamContext* GetFor(BrowserContext* browser_context);
-
-  void InitializeOnIOThread();
-
-  StreamRegistry* registry() const { return registry_.get(); }
-
- protected:
-  virtual ~StreamContext();
-
- private:
-  friend class base::DeleteHelper<StreamContext>;
-  friend class base::RefCountedThreadSafe<StreamContext,
-                                          StreamContextDeleter>;
-  friend struct StreamContextDeleter;
-
-  void DeleteOnCorrectThread() const;
-
-  std::unique_ptr<StreamRegistry> registry_;
-};
-
-struct StreamContextDeleter {
-  static void Destruct(const StreamContext* context) {
-    context->DeleteOnCorrectThread();
-  }
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_STREAMS_STREAM_CONTEXT_H_
diff --git a/content/browser/streams/stream_handle_impl.cc b/content/browser/streams/stream_handle_impl.cc
deleted file mode 100644
index 03e17b5..0000000
--- a/content/browser/streams/stream_handle_impl.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/streams/stream_handle_impl.h"
-
-#include <stddef.h>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "content/browser/streams/stream.h"
-
-namespace content {
-
-namespace {
-
-void RunCloseListeners(const std::vector<base::Closure>& close_listeners) {
-  for (size_t i = 0; i < close_listeners.size(); ++i)
-    close_listeners[i].Run();
-}
-
-}  // namespace
-
-StreamHandleImpl::StreamHandleImpl(const base::WeakPtr<Stream>& stream)
-    : stream_(stream),
-      url_(stream->url()),
-      stream_task_runner_(base::ThreadTaskRunnerHandle::Get().get()) {
-}
-
-StreamHandleImpl::~StreamHandleImpl() {
-  stream_task_runner_->PostTaskAndReply(
-      FROM_HERE, base::BindOnce(&Stream::CloseHandle, stream_),
-      base::BindOnce(&RunCloseListeners, close_listeners_));
-}
-
-const GURL& StreamHandleImpl::GetURL() {
-  return url_;
-}
-
-void StreamHandleImpl::AddCloseListener(const base::Closure& callback) {
-  close_listeners_.push_back(callback);
-}
-
-}  // namespace content
diff --git a/content/browser/streams/stream_handle_impl.h b/content/browser/streams/stream_handle_impl.h
deleted file mode 100644
index 15e8e20..0000000
--- a/content/browser/streams/stream_handle_impl.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_STREAMS_STREAM_HANDLE_IMPL_H_
-#define CONTENT_BROWSER_STREAMS_STREAM_HANDLE_IMPL_H_
-
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "content/public/browser/stream_handle.h"
-
-namespace base {
-class SingleThreadTaskRunner;
-}
-
-namespace content {
-
-class Stream;
-
-class StreamHandleImpl : public StreamHandle {
- public:
-  StreamHandleImpl(const base::WeakPtr<Stream>& stream);
-  ~StreamHandleImpl() override;
-
- private:
-  // StreamHandle overrides
-  const GURL& GetURL() override;
-  void AddCloseListener(const base::Closure& callback) override;
-
-  base::WeakPtr<Stream> stream_;
-  GURL url_;
-  base::SingleThreadTaskRunner* stream_task_runner_;
-  std::vector<base::Closure> close_listeners_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_STREAMS_STREAM_HANDLE_IMPL_H_
-
diff --git a/content/browser/streams/stream_metadata.h b/content/browser/streams/stream_metadata.h
deleted file mode 100644
index a53424ac..0000000
--- a/content/browser/streams/stream_metadata.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_STREAMS_STREAM_METADATA_H_
-#define CONTENT_BROWSER_STREAMS_STREAM_METADATA_H_
-
-#include "base/macros.h"
-
-#include "net/http/http_response_info.h"
-
-namespace content {
-
-class StreamMetadata {
- public:
-  explicit StreamMetadata(const net::HttpResponseInfo& response_info)
-      : total_received_bytes_(0),
-        raw_body_bytes_(0),
-        response_info_(response_info) {}
-
-  void set_total_received_bytes(int64_t bytes) {
-    total_received_bytes_ = bytes;
-  }
-  int64_t total_received_bytes() const { return total_received_bytes_; }
-
-  void set_raw_body_bytes(int64_t bytes) { raw_body_bytes_ = bytes; }
-  int64_t raw_body_bytes() const { return raw_body_bytes_; }
-
-  const net::HttpResponseInfo& response_info() const { return response_info_; }
-
- private:
-  int64_t total_received_bytes_;
-  int64_t raw_body_bytes_;
-  net::HttpResponseInfo response_info_;
-
-  DISALLOW_COPY_AND_ASSIGN(StreamMetadata);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_STREAMS_STREAM_METADATA_H_
\ No newline at end of file
diff --git a/content/browser/streams/stream_read_observer.h b/content/browser/streams/stream_read_observer.h
deleted file mode 100644
index 08fd657..0000000
--- a/content/browser/streams/stream_read_observer.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_STREAMS_STREAM_READ_OBSERVER_H_
-#define CONTENT_BROWSER_STREAMS_STREAM_READ_OBSERVER_H_
-
-#include "content/common/content_export.h"
-
-namespace content {
-
-class Stream;
-
-class CONTENT_EXPORT StreamReadObserver {
- public:
-  // Sent when there is data available to be read from the stream.
-  virtual void OnDataAvailable(Stream* stream) = 0;
-
- protected:
-  virtual ~StreamReadObserver() {}
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_STREAMS_STREAM_READ_OBSERVER_H_
-
diff --git a/content/browser/streams/stream_register_observer.h b/content/browser/streams/stream_register_observer.h
deleted file mode 100644
index ed6a299..0000000
--- a/content/browser/streams/stream_register_observer.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_STREAMS_STREAM_REGISTER_OBSERVER_H_
-#define CONTENT_BROWSER_STREAMS_STREAM_REGISTER_OBSERVER_H_
-
-#include "content/common/content_export.h"
-
-namespace content {
-
-class Stream;
-
-class CONTENT_EXPORT StreamRegisterObserver {
- public:
-  // Sent when the stream is registered.
-  virtual void OnStreamRegistered(Stream* stream) = 0;
-
- protected:
-  virtual ~StreamRegisterObserver() {}
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_STREAMS_STREAM_REGISTER_OBSERVER_H_
diff --git a/content/browser/streams/stream_registry.cc b/content/browser/streams/stream_registry.cc
deleted file mode 100644
index a905f48..0000000
--- a/content/browser/streams/stream_registry.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/streams/stream_registry.h"
-
-#include "content/browser/streams/stream.h"
-
-namespace content {
-
-namespace {
-// The maximum size of memory each StreamRegistry instance is allowed to use
-// for its Stream instances.
-const size_t kDefaultMaxMemoryUsage = 1024 * 1024 * 1024U;  // 1GiB
-}
-
-StreamRegistry::StreamRegistry()
-    : total_memory_usage_(0),
-      max_memory_usage_(kDefaultMaxMemoryUsage) {
-}
-
-StreamRegistry::~StreamRegistry() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(register_observers_.empty());
-}
-
-void StreamRegistry::RegisterStream(Stream* stream) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(stream);
-  DCHECK(!stream->url().is_empty());
-
-  auto aborted_url_itr = reader_aborted_urls_.find(stream->url());
-  if (aborted_url_itr != reader_aborted_urls_.end()) {
-    reader_aborted_urls_.erase(aborted_url_itr);
-    return;
-  }
-  streams_[stream->url()] = stream;
-
-  auto itr = register_observers_.find(stream->url());
-  if (itr != register_observers_.end())
-    itr->second->OnStreamRegistered(stream);
-}
-
-scoped_refptr<Stream> StreamRegistry::GetStream(const GURL& url) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  StreamMap::const_iterator stream = streams_.find(url);
-  if (stream != streams_.end())
-    return stream->second;
-
-  return nullptr;
-}
-
-bool StreamRegistry::CloneStream(const GURL& url, const GURL& src_url) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  scoped_refptr<Stream> stream(GetStream(src_url));
-  if (stream.get()) {
-    streams_[url] = stream;
-    return true;
-  }
-  return false;
-}
-
-void StreamRegistry::UnregisterStream(const GURL& url) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  auto iter = streams_.find(url);
-  if (iter == streams_.end())
-    return;
-
-  // Only update |total_memory_usage_| if |url| is NOT a Stream clone because
-  // cloned streams do not update |total_memory_usage_|.
-  if (iter->second->url() == url) {
-    size_t buffered_bytes = iter->second->last_total_buffered_bytes();
-    DCHECK_LE(buffered_bytes, total_memory_usage_);
-    total_memory_usage_ -= buffered_bytes;
-  }
-
-  streams_.erase(url);
-}
-
-bool StreamRegistry::UpdateMemoryUsage(const GURL& url,
-                                       size_t current_size,
-                                       size_t increase) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  auto iter = streams_.find(url);
-  // A Stream must be registered with its parent registry to get memory.
-  if (iter == streams_.end())
-    return false;
-
-  size_t last_size = iter->second->last_total_buffered_bytes();
-  DCHECK_LE(last_size, total_memory_usage_);
-  size_t usage_of_others = total_memory_usage_ - last_size;
-  DCHECK_LE(current_size, last_size);
-  size_t current_total_memory_usage = usage_of_others + current_size;
-
-  if (increase > max_memory_usage_ - current_total_memory_usage)
-    return false;
-
-  total_memory_usage_ = current_total_memory_usage + increase;
-  return true;
-}
-
-
-void StreamRegistry::SetRegisterObserver(const GURL& url,
-                                         StreamRegisterObserver* observer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(register_observers_.find(url) == register_observers_.end());
-  register_observers_[url] = observer;
-}
-
-void StreamRegistry::RemoveRegisterObserver(const GURL& url) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  register_observers_.erase(url);
-}
-
-void StreamRegistry::AbortPendingStream(const GURL& url) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  reader_aborted_urls_.insert(url);
-}
-
-}  // namespace content
diff --git a/content/browser/streams/stream_registry.h b/content/browser/streams/stream_registry.h
deleted file mode 100644
index 6ef17b1..0000000
--- a/content/browser/streams/stream_registry.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_STREAMS_STREAM_REGISTRY_H_
-#define CONTENT_BROWSER_STREAMS_STREAM_REGISTRY_H_
-
-#include <stddef.h>
-
-#include <map>
-#include <set>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/sequence_checker.h"
-#include "content/browser/streams/stream_register_observer.h"
-#include "content/common/content_export.h"
-#include "url/gurl.h"
-
-namespace content {
-
-class Stream;
-
-// Maintains a mapping of blob: URLs to active streams.
-class CONTENT_EXPORT StreamRegistry {
- public:
-  StreamRegistry();
-  virtual ~StreamRegistry();
-
-  // Registers a stream, and sets its URL.
-  void RegisterStream(Stream* stream);
-
-  // Clones a stream.  Returns true on success, or false if |src_url| doesn't
-  // exist.
-  bool CloneStream(const GURL& url, const GURL& src_url);
-
-  void UnregisterStream(const GURL& url);
-
-  // Called by Stream instances to request increase of memory usage. If the
-  // total memory usage for this registry is going to exceed the limit,
-  // returns false. Otherwise, updates |total_memory_usage_| and returns true.
-  //
-  // |current_size| is the up-to-date size of ByteStream of the Stream instance
-  // and |increase| must be the amount of data going to be added to the Stream
-  // instance.
-  bool UpdateMemoryUsage(const GURL& url, size_t current_size, size_t increase);
-
-  // Gets the stream associated with |url|.  Returns NULL if there is no such
-  // stream.
-  scoped_refptr<Stream> GetStream(const GURL& url);
-
-  void set_max_memory_usage_for_testing(size_t size) {
-    max_memory_usage_ = size;
-  }
-
-  void SetRegisterObserver(const GURL& url, StreamRegisterObserver* observer);
-  void RemoveRegisterObserver(const GURL& url);
-
-  // If the reader is aborted before the stream is registered, call this method
-  // to reduce the memory consumption. After this method is called,
-  // RegisterStream doesn't register the stream of the URL.
-  void AbortPendingStream(const GURL& url);
-
- private:
-  typedef std::map<GURL, scoped_refptr<Stream> > StreamMap;
-
-  StreamMap streams_;
-  std::map<GURL, StreamRegisterObserver*> register_observers_;
-  std::set<GURL> reader_aborted_urls_;
-
-  size_t total_memory_usage_;
-
-  // Maximum amount of memory allowed to use for Stream instances registered
-  // with this registry.
-  size_t max_memory_usage_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(StreamRegistry);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_STREAMS_STREAM_REGISTRY_H_
diff --git a/content/browser/streams/stream_unittest.cc b/content/browser/streams/stream_unittest.cc
deleted file mode 100644
index e256944..0000000
--- a/content/browser/streams/stream_unittest.cc
+++ /dev/null
@@ -1,421 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-
-#include "base/run_loop.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/test/test_simple_task_runner.h"
-#include "content/browser/streams/stream.h"
-#include "content/browser/streams/stream_read_observer.h"
-#include "content/browser/streams/stream_register_observer.h"
-#include "content/browser/streams/stream_registry.h"
-#include "content/browser/streams/stream_write_observer.h"
-#include "net/base/net_errors.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace content {
-
-class StreamTest : public testing::Test {
- public:
-  StreamTest() : producing_seed_key_(0) {}
-
-  void SetUp() override { registry_.reset(new StreamRegistry()); }
-
-  // Create a new IO buffer of the given |buffer_size| and fill it with random
-  // data.
-  scoped_refptr<net::IOBuffer> NewIOBuffer(size_t buffer_size) {
-    scoped_refptr<net::IOBuffer> buffer =
-        base::MakeRefCounted<net::IOBuffer>(buffer_size);
-    char *bufferp = buffer->data();
-    for (size_t i = 0; i < buffer_size; i++)
-      bufferp[i] = (i + producing_seed_key_) % (1 << sizeof(char));
-    ++producing_seed_key_;
-    return buffer;
-  }
-
- protected:
-  base::test::ScopedTaskEnvironment task_environment_;
-  std::unique_ptr<StreamRegistry> registry_;
-
- private:
-  int producing_seed_key_;
-};
-
-class TestStreamReader : public StreamReadObserver {
- public:
-  TestStreamReader() : buffer_(base::MakeRefCounted<net::GrowableIOBuffer>()) {}
-  ~TestStreamReader() override {}
-
-  void Read(Stream* stream) {
-    const size_t kBufferSize = 32768;
-    scoped_refptr<net::IOBuffer> buffer =
-        base::MakeRefCounted<net::IOBuffer>(kBufferSize);
-
-    int bytes_read = 0;
-    while (true) {
-      Stream::StreamState state =
-          stream->ReadRawData(buffer.get(), kBufferSize, &bytes_read);
-      switch (state) {
-        case Stream::STREAM_HAS_DATA:
-          // TODO(tyoshino): Move these expectations to the beginning of Read()
-          // method once Stream::Finalize() is fixed.
-          EXPECT_FALSE(completed_);
-          break;
-        case Stream::STREAM_COMPLETE:
-          completed_ = true;
-          status_ = stream->GetStatus();
-          return;
-        case Stream::STREAM_EMPTY:
-          EXPECT_FALSE(completed_);
-          return;
-        case Stream::STREAM_ABORTED:
-          aborted_ = true;
-          EXPECT_FALSE(completed_);
-          return;
-      }
-      size_t old_capacity = buffer_->capacity();
-      buffer_->SetCapacity(old_capacity + bytes_read);
-      memcpy(buffer_->StartOfBuffer() + old_capacity,
-             buffer->data(), bytes_read);
-    }
-  }
-
-  void OnDataAvailable(Stream* stream) override { Read(stream); }
-
-  scoped_refptr<net::GrowableIOBuffer> buffer() { return buffer_; }
-
-  bool completed() const { return completed_; }
-  bool aborted() const { return aborted_; }
-  int status() const { return status_; }
-
- private:
-  scoped_refptr<net::GrowableIOBuffer> buffer_;
-  bool completed_ = false;
-  bool aborted_ = false;
-  int status_ = 0;
-};
-
-class TestStreamWriter : public StreamWriteObserver {
- public:
-  TestStreamWriter() {}
-  ~TestStreamWriter() override {}
-
-  void Write(Stream* stream,
-             scoped_refptr<net::IOBuffer> buffer,
-             size_t buffer_size) {
-    stream->AddData(buffer, buffer_size);
-  }
-
-  void OnSpaceAvailable(Stream* stream) override {}
-
-  void OnClose(Stream* stream) override {}
-};
-
-class TestStreamObserver : public StreamRegisterObserver {
- public:
-  TestStreamObserver(const GURL& url, StreamRegistry* registry)
-      : url_(url), registry_(registry), registered_(false), stream_(nullptr) {
-    registry->SetRegisterObserver(url, this);
-  }
-  ~TestStreamObserver() override { registry_->RemoveRegisterObserver(url_); }
-  void OnStreamRegistered(Stream* stream) override {
-    registered_ = true;
-    stream_ = stream;
-  }
-  bool registered() const { return registered_; }
-  Stream* stream() const { return stream_; }
-
- private:
-  const GURL url_;
-  StreamRegistry* registry_;
-  bool registered_;
-  Stream* stream_;
-};
-
-TEST_F(StreamTest, SetAndRemoveRegisterObserver) {
-  TestStreamWriter writer1;
-  TestStreamWriter writer2;
-  GURL url1("blob://stream1");
-  GURL url2("blob://stream2");
-  std::unique_ptr<TestStreamObserver> observer1(
-      new TestStreamObserver(url1, registry_.get()));
-  std::unique_ptr<TestStreamObserver> observer2(
-      new TestStreamObserver(url2, registry_.get()));
-  scoped_refptr<Stream> stream1(new Stream(registry_.get(), &writer1, url1));
-  EXPECT_TRUE(observer1->registered());
-  EXPECT_EQ(observer1->stream(), stream1.get());
-  EXPECT_FALSE(observer2->registered());
-
-  observer2.reset();
-  scoped_refptr<Stream> stream2(new Stream(registry_.get(), &writer2, url2));
-}
-
-TEST_F(StreamTest, SetReadObserver) {
-  TestStreamReader reader;
-  TestStreamWriter writer;
-
-  GURL url("blob://stream");
-  scoped_refptr<Stream> stream(
-      new Stream(registry_.get(), &writer, url));
-  EXPECT_TRUE(stream->SetReadObserver(&reader));
-}
-
-TEST_F(StreamTest, SetReadObserver_SecondFails) {
-  TestStreamReader reader1;
-  TestStreamReader reader2;
-  TestStreamWriter writer;
-
-  GURL url("blob://stream");
-  scoped_refptr<Stream> stream(
-      new Stream(registry_.get(), &writer, url));
-  EXPECT_TRUE(stream->SetReadObserver(&reader1));
-  EXPECT_FALSE(stream->SetReadObserver(&reader2));
-}
-
-TEST_F(StreamTest, SetReadObserver_TwoReaders) {
-  TestStreamReader reader1;
-  TestStreamReader reader2;
-  TestStreamWriter writer;
-
-  GURL url("blob://stream");
-  scoped_refptr<Stream> stream(
-      new Stream(registry_.get(), &writer, url));
-  EXPECT_TRUE(stream->SetReadObserver(&reader1));
-
-  // Once the first read observer is removed, a new one can be added.
-  stream->RemoveReadObserver(&reader1);
-  EXPECT_TRUE(stream->SetReadObserver(&reader2));
-}
-
-TEST_F(StreamTest, Stream) {
-  TestStreamReader reader;
-  TestStreamWriter writer;
-
-  GURL url("blob://stream");
-  scoped_refptr<Stream> stream(
-      new Stream(registry_.get(), &writer, url));
-  EXPECT_TRUE(stream->SetReadObserver(&reader));
-
-  const int kBufferSize = 1000000;
-  scoped_refptr<net::IOBuffer> buffer(NewIOBuffer(kBufferSize));
-  writer.Write(stream.get(), buffer, kBufferSize);
-  stream->Finalize(net::OK);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(reader.completed());
-  EXPECT_EQ(net::OK, reader.status());
-
-  ASSERT_EQ(reader.buffer()->capacity(), kBufferSize);
-  for (int i = 0; i < kBufferSize; i++)
-    EXPECT_EQ(buffer->data()[i], reader.buffer()->data()[i]);
-}
-
-TEST_F(StreamTest, Abort) {
-  TestStreamReader reader;
-  TestStreamWriter writer;
-
-  GURL url("blob://stream");
-  scoped_refptr<Stream> stream(new Stream(registry_.get(), &writer, url));
-  EXPECT_TRUE(stream->SetReadObserver(&reader));
-
-  stream->Abort();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(reader.completed());
-  EXPECT_TRUE(reader.aborted());
-}
-
-TEST_F(StreamTest, Error) {
-  TestStreamReader reader;
-  TestStreamWriter writer;
-
-  GURL url("blob://stream");
-  scoped_refptr<Stream> stream(new Stream(registry_.get(), &writer, url));
-  EXPECT_TRUE(stream->SetReadObserver(&reader));
-
-  stream->Finalize(net::ERR_ACCESS_DENIED);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(reader.completed());
-  EXPECT_EQ(net::ERR_ACCESS_DENIED, reader.status());
-}
-
-// Test that even if a reader receives an empty buffer, once TransferData()
-// method is called on it with |source_complete| = true, following Read() calls
-// on it never returns STREAM_EMPTY. Together with StreamTest.Stream above, this
-// guarantees that Reader::Read() call returns only STREAM_HAS_DATA
-// or STREAM_COMPLETE in |data_available_callback_| call corresponding to
-// Writer::Close().
-TEST_F(StreamTest, ClosedReaderDoesNotReturnStreamEmpty) {
-  TestStreamReader reader;
-  TestStreamWriter writer;
-
-  GURL url("blob://stream");
-  scoped_refptr<Stream> stream(
-      new Stream(registry_.get(), &writer, url));
-  EXPECT_TRUE(stream->SetReadObserver(&reader));
-
-  const int kBufferSize = 0;
-  scoped_refptr<net::IOBuffer> buffer(NewIOBuffer(kBufferSize));
-  stream->AddData(buffer, kBufferSize);
-  stream->Finalize(net::OK);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(reader.completed());
-  EXPECT_EQ(0, reader.buffer()->capacity());
-  EXPECT_EQ(net::OK, reader.status());
-}
-
-TEST_F(StreamTest, GetStream) {
-  TestStreamWriter writer;
-
-  GURL url("blob://stream");
-  scoped_refptr<Stream> stream1(
-      new Stream(registry_.get(), &writer, url));
-
-  scoped_refptr<Stream> stream2 = registry_->GetStream(url);
-  ASSERT_EQ(stream1, stream2);
-}
-
-TEST_F(StreamTest, GetStream_Missing) {
-  TestStreamWriter writer;
-
-  GURL url1("blob://stream");
-  scoped_refptr<Stream> stream1(
-      new Stream(registry_.get(), &writer, url1));
-
-  GURL url2("blob://stream2");
-  scoped_refptr<Stream> stream2 = registry_->GetStream(url2);
-  ASSERT_FALSE(stream2.get());
-}
-
-TEST_F(StreamTest, CloneStream) {
-  TestStreamWriter writer;
-
-  GURL url1("blob://stream");
-  scoped_refptr<Stream> stream1(
-      new Stream(registry_.get(), &writer, url1));
-
-  GURL url2("blob://stream2");
-  ASSERT_TRUE(registry_->CloneStream(url2, url1));
-  scoped_refptr<Stream> stream2 = registry_->GetStream(url2);
-  ASSERT_EQ(stream1, stream2);
-}
-
-TEST_F(StreamTest, CloneStream_Missing) {
-  TestStreamWriter writer;
-
-  GURL url1("blob://stream");
-  scoped_refptr<Stream> stream1(
-      new Stream(registry_.get(), &writer, url1));
-
-  GURL url2("blob://stream2");
-  GURL url3("blob://stream3");
-  ASSERT_FALSE(registry_->CloneStream(url2, url3));
-  scoped_refptr<Stream> stream2 = registry_->GetStream(url2);
-  ASSERT_FALSE(stream2.get());
-}
-
-TEST_F(StreamTest, UnregisterStream) {
-  TestStreamWriter writer;
-
-  GURL url("blob://stream");
-  scoped_refptr<Stream> stream1(
-      new Stream(registry_.get(), &writer, url));
-
-  registry_->UnregisterStream(url);
-  scoped_refptr<Stream> stream2 = registry_->GetStream(url);
-  ASSERT_FALSE(stream2.get());
-}
-
-TEST_F(StreamTest, MemoryExceedMemoryUsageLimit) {
-  TestStreamWriter writer1;
-  TestStreamWriter writer2;
-
-  GURL url1("blob://stream");
-  scoped_refptr<Stream> stream1(
-      new Stream(registry_.get(), &writer1, url1));
-
-  GURL url2("blob://stream2");
-  scoped_refptr<Stream> stream2(
-      new Stream(registry_.get(), &writer2, url2));
-
-  const int kMaxMemoryUsage = 1500000;
-  registry_->set_max_memory_usage_for_testing(kMaxMemoryUsage);
-
-  const int kBufferSize = 1000000;
-  scoped_refptr<net::IOBuffer> buffer(NewIOBuffer(kBufferSize));
-  writer1.Write(stream1.get(), buffer, kBufferSize);
-  // Make transfer happen.
-  base::RunLoop().RunUntilIdle();
-
-  writer2.Write(stream2.get(), buffer, kBufferSize);
-
-  // Written data (1000000 * 2) exceeded limit (1500000). |stream2| should be
-  // unregistered with |registry_|.
-  EXPECT_EQ(nullptr, registry_->GetStream(url2).get());
-
-  writer1.Write(stream1.get(), buffer, kMaxMemoryUsage - kBufferSize);
-  // Should be accepted since stream2 is unregistered and the new data is not
-  // so big to exceed the limit.
-  EXPECT_FALSE(registry_->GetStream(url1).get() == nullptr);
-}
-
-TEST_F(StreamTest, UnderMemoryUsageLimit) {
-  TestStreamWriter writer;
-  TestStreamReader reader;
-
-  GURL url("blob://stream");
-  scoped_refptr<Stream> stream(new Stream(registry_.get(), &writer, url));
-  EXPECT_TRUE(stream->SetReadObserver(&reader));
-
-  registry_->set_max_memory_usage_for_testing(1500000);
-
-  const int kBufferSize = 1000000;
-  scoped_refptr<net::IOBuffer> buffer(NewIOBuffer(kBufferSize));
-  writer.Write(stream.get(), buffer, kBufferSize);
-
-  // Run loop to make |reader| consume the data.
-  base::RunLoop().RunUntilIdle();
-
-  writer.Write(stream.get(), buffer, kBufferSize);
-
-  EXPECT_EQ(stream.get(), registry_->GetStream(url).get());
-}
-
-TEST_F(StreamTest, Flush) {
-  TestStreamWriter writer;
-  TestStreamReader reader;
-
-  GURL url("blob://stream");
-  scoped_refptr<Stream> stream(new Stream(registry_.get(), &writer, url));
-  EXPECT_TRUE(stream->SetReadObserver(&reader));
-
-  // If the written data size is smaller than ByteStreamWriter's (total size /
-  // kFractionBufferBeforeSending), StreamReadObserver::OnDataAvailable is not
-  // called.
-  const int kBufferSize = 1;
-  scoped_refptr<net::IOBuffer> buffer(NewIOBuffer(kBufferSize));
-  writer.Write(stream.get(), buffer, kBufferSize);
-
-  // Run loop to make |reader| consume the data.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(0, reader.buffer()->capacity());
-
-  stream->Flush();
-
-  // Run loop to make |reader| consume the data.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kBufferSize, reader.buffer()->capacity());
-
-  EXPECT_EQ(stream.get(), registry_->GetStream(url).get());
-}
-
-TEST_F(StreamTest, AbortPendingStream) {
-  TestStreamWriter writer;
-
-  GURL url("blob://stream");
-  registry_->AbortPendingStream(url);
-  scoped_refptr<Stream> stream1(new Stream(registry_.get(), &writer, url));
-  ASSERT_EQ(nullptr, registry_->GetStream(url).get());
-}
-
-}  // namespace content
diff --git a/content/browser/streams/stream_url_request_job.cc b/content/browser/streams/stream_url_request_job.cc
deleted file mode 100644
index 0fc62ae..0000000
--- a/content/browser/streams/stream_url_request_job.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/streams/stream_url_request_job.h"
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "content/browser/streams/stream.h"
-#include "content/browser/streams/stream_metadata.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/http/http_byte_range.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_response_info.h"
-#include "net/http/http_util.h"
-#include "net/url_request/url_request.h"
-
-namespace content {
-
-StreamURLRequestJob::StreamURLRequestJob(net::URLRequest* request,
-                                         net::NetworkDelegate* network_delegate,
-                                         scoped_refptr<Stream> stream)
-    : net::URLRangeRequestJob(request, network_delegate),
-      stream_(stream),
-      pending_buffer_size_(0),
-      total_bytes_read_(0),
-      raw_body_bytes_(0),
-      max_range_(0),
-      request_failed_(false),
-      error_code_(net::OK),
-      weak_factory_(this) {
-  DCHECK(stream_.get());
-  stream_->SetReadObserver(this);
-}
-
-StreamURLRequestJob::~StreamURLRequestJob() {
-  ClearStream();
-}
-
-void StreamURLRequestJob::OnDataAvailable(Stream* stream) {
-  // Do nothing if pending_buffer_ is empty, i.e. there's no ReadRawData()
-  // operation waiting for IO completion.
-  if (!pending_buffer_.get())
-    return;
-
-  // pending_buffer_ is set to the IOBuffer instance provided to ReadRawData()
-  // by URLRequestJob.
-
-  int result = 0;
-  switch (stream_->ReadRawData(pending_buffer_.get(), pending_buffer_size_,
-                               &result)) {
-    case Stream::STREAM_HAS_DATA:
-      DCHECK_GT(result, 0);
-      break;
-    case Stream::STREAM_COMPLETE:
-      // Ensure ReadRawData gives net::OK.
-      DCHECK_EQ(net::OK, result);
-      break;
-    case Stream::STREAM_EMPTY:
-      NOTREACHED();
-      break;
-    case Stream::STREAM_ABORTED:
-      // Handle this as connection reset.
-      result = net::ERR_CONNECTION_RESET;
-      break;
-  }
-
-  // Clear the buffers before notifying the read is complete, so that it is
-  // safe for the observer to read.
-  pending_buffer_ = nullptr;
-  pending_buffer_size_ = 0;
-
-  if (result > 0)
-    total_bytes_read_ += result;
-  ReadRawDataComplete(result);
-}
-
-// net::URLRequestJob methods.
-void StreamURLRequestJob::Start() {
-  // Continue asynchronously.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&StreamURLRequestJob::DidStart,
-                                weak_factory_.GetWeakPtr()));
-}
-
-void StreamURLRequestJob::Kill() {
-  net::URLRequestJob::Kill();
-  weak_factory_.InvalidateWeakPtrs();
-  ClearStream();
-}
-
-int StreamURLRequestJob::ReadRawData(net::IOBuffer* buf, int buf_size) {
-  if (request_failed_)
-    return error_code_;
-
-  DCHECK(buf);
-  int to_read = buf_size;
-  if (max_range_ && to_read) {
-    if (to_read + total_bytes_read_ > max_range_)
-      to_read = max_range_ - total_bytes_read_;
-
-    if (to_read == 0)
-      return 0;
-  }
-
-  int bytes_read = 0;
-  switch (stream_->ReadRawData(buf, to_read, &bytes_read)) {
-    case Stream::STREAM_HAS_DATA:
-      total_bytes_read_ += bytes_read;
-      raw_body_bytes_ = stream_->metadata()->raw_body_bytes();
-      return bytes_read;
-    case Stream::STREAM_COMPLETE:
-      return stream_->GetStatus();
-    case Stream::STREAM_EMPTY:
-      pending_buffer_ = buf;
-      pending_buffer_size_ = to_read;
-      return net::ERR_IO_PENDING;
-    case Stream::STREAM_ABORTED:
-      // Handle this as connection reset.
-      return net::ERR_CONNECTION_RESET;
-  }
-  NOTREACHED();
-  return net::ERR_FAILED;
-}
-
-bool StreamURLRequestJob::GetMimeType(std::string* mime_type) const {
-  const StreamMetadata* metadata = stream_->metadata();
-  if (!metadata || !metadata->response_info().headers)
-    return false;
-
-  // TODO(zork): Support registered MIME types if needed.
-  return metadata->response_info().headers->GetMimeType(mime_type);
-}
-
-void StreamURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
-  if (failed_response_info_) {
-    *info = *failed_response_info_;
-    return;
-  }
-  const StreamMetadata* metadata = stream_->metadata();
-  if (metadata)
-    *info = metadata->response_info();
-}
-
-int64_t StreamURLRequestJob::GetTotalReceivedBytes() const {
-  if (!stream_)
-    return 0;
-  const StreamMetadata* metadata = stream_->metadata();
-  if (!metadata)
-    return 0;
-  return metadata->total_received_bytes();
-}
-
-int64_t StreamURLRequestJob::prefilter_bytes_read() const {
-  return raw_body_bytes_;
-}
-
-void StreamURLRequestJob::DidStart() {
-  if (range_parse_result() == net::OK && ranges().size() > 0) {
-    // Only one range is supported, and it must start at the first byte.
-    if (ranges().size() > 1 || ranges()[0].first_byte_position() != 0) {
-      NotifyMethodNotSupported();
-      return;
-    }
-
-    max_range_ = ranges()[0].last_byte_position() + 1;
-  }
-
-  // This class only supports GET requests.
-  if (request()->method() != "GET") {
-    NotifyMethodNotSupported();
-    return;
-  }
-  NotifyHeadersComplete();
-}
-
-void StreamURLRequestJob::NotifyMethodNotSupported() {
-  const net::HttpStatusCode status_code = net::HTTP_METHOD_NOT_ALLOWED;
-  request_failed_ = true;
-  error_code_ = net::ERR_METHOD_NOT_SUPPORTED;
-
-  std::string status("HTTP/1.1 ");
-  status.append(base::NumberToString(status_code));
-  status.append(" ");
-  status.append(net::GetHttpReasonPhrase(status_code));
-  status.append("\0\0", 2);
-  net::HttpResponseHeaders* headers = new net::HttpResponseHeaders(status);
-
-  failed_response_info_.reset(new net::HttpResponseInfo());
-  failed_response_info_->headers = headers;
-  NotifyHeadersComplete();
-}
-
-void StreamURLRequestJob::ClearStream() {
-  if (stream_.get()) {
-    stream_->RemoveReadObserver(this);
-    stream_ = nullptr;
-  }
-}
-
-}  // namespace content
diff --git a/content/browser/streams/stream_url_request_job.h b/content/browser/streams/stream_url_request_job.h
deleted file mode 100644
index 170bdaf..0000000
--- a/content/browser/streams/stream_url_request_job.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_STREAMS_STREAM_URL_REQUEST_JOB_H_
-#define CONTENT_BROWSER_STREAMS_STREAM_URL_REQUEST_JOB_H_
-
-#include "base/macros.h"
-#include "content/browser/streams/stream_read_observer.h"
-#include "content/common/content_export.h"
-#include "net/http/http_status_code.h"
-#include "net/url_request/url_range_request_job.h"
-
-namespace content {
-
-class Stream;
-
-// A request job that handles reading stream URLs.
-class CONTENT_EXPORT StreamURLRequestJob
-    : public net::URLRangeRequestJob,
-      public StreamReadObserver {
- public:
-  StreamURLRequestJob(net::URLRequest* request,
-                      net::NetworkDelegate* network_delegate,
-                      scoped_refptr<Stream> stream);
-
-  // StreamObserver methods.
-  void OnDataAvailable(Stream* stream) override;
-
-  // net::URLRequestJob methods.
-  void Start() override;
-  void Kill() override;
-  int ReadRawData(net::IOBuffer* buf, int buf_size) override;
-  bool GetMimeType(std::string* mime_type) const override;
-  void GetResponseInfo(net::HttpResponseInfo* info) override;
-  int64_t GetTotalReceivedBytes() const override;
-  int64_t prefilter_bytes_read() const override;
-
- protected:
-  ~StreamURLRequestJob() override;
-
- private:
-  void DidStart();
-  void NotifyMethodNotSupported();
-  void UpdateNetworkBytesRead();
-  void ClearStream();
-
-  scoped_refptr<content::Stream> stream_;
-  scoped_refptr<net::IOBuffer> pending_buffer_;
-  int pending_buffer_size_;
-
-  // Total bytes received for this job.
-  int total_bytes_read_;
-  int64_t raw_body_bytes_;
-
-  int max_range_;
-  bool request_failed_;
-  std::unique_ptr<net::HttpResponseInfo> failed_response_info_;
-  int error_code_;  // Only set if request_failed_.
-
-  base::WeakPtrFactory<StreamURLRequestJob> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(StreamURLRequestJob);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_STREAMS_STREAM_URL_REQUEST_JOB_H_
diff --git a/content/browser/streams/stream_url_request_job_unittest.cc b/content/browser/streams/stream_url_request_job_unittest.cc
deleted file mode 100644
index 92083246..0000000
--- a/content/browser/streams/stream_url_request_job_unittest.cc
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/streams/stream_url_request_job.h"
-
-#include "base/run_loop.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/test/test_simple_task_runner.h"
-#include "content/browser/streams/stream.h"
-#include "content/browser/streams/stream_metadata.h"
-#include "content/browser/streams/stream_registry.h"
-#include "content/browser/streams/stream_write_observer.h"
-#include "net/base/request_priority.h"
-#include "net/http/http_byte_range.h"
-#include "net/http/http_response_headers.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_job_factory_impl.h"
-#include "net/url_request/url_request_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace content {
-
-namespace {
-
-const int kBufferSize = 1024;
-const char kTestData1[] = "Hello";
-const char kTestData2[] = "Here it is data.";
-
-const GURL kStreamURL("blob://stream");
-
-}  // namespace
-
-class StreamURLRequestJobTest : public testing::Test {
- public:
-  // A simple ProtocolHandler implementation to create StreamURLRequestJob.
-  class MockProtocolHandler :
-      public net::URLRequestJobFactory::ProtocolHandler {
-   public:
-    MockProtocolHandler(StreamRegistry* registry) : registry_(registry) {}
-
-    // net::URLRequestJobFactory::ProtocolHandler override.
-    net::URLRequestJob* MaybeCreateJob(
-        net::URLRequest* request,
-        net::NetworkDelegate* network_delegate) const override {
-      scoped_refptr<Stream> stream = registry_->GetStream(request->url());
-      if (stream.get())
-        return new StreamURLRequestJob(request, network_delegate, stream);
-      return nullptr;
-    }
-
-   private:
-    StreamRegistry* registry_;
-  };
-
-  StreamURLRequestJobTest() {}
-
-  void SetUp() override {
-    registry_.reset(new StreamRegistry());
-
-    url_request_job_factory_.SetProtocolHandler(
-        "blob", std::make_unique<MockProtocolHandler>(registry_.get()));
-    url_request_context_.set_job_factory(&url_request_job_factory_);
-  }
-
-  void TearDown() override {}
-
-  void TestSuccessRequest(const GURL& url,
-                          const std::string& expected_response) {
-    TestRequest("GET", url, net::HttpRequestHeaders(), 200, net::OK,
-                expected_response);
-  }
-
-  std::unique_ptr<net::HttpResponseInfo> BuildResponseInfo() {
-    auto response_info = std::make_unique<net::HttpResponseInfo>();
-    response_info->headers = new net::HttpResponseHeaders("HTTP/1.1 200 OK");
-    return response_info;
-  }
-
-  void TestRequest(const std::string& method,
-                   const GURL& url,
-                   const net::HttpRequestHeaders& extra_headers,
-                   int expected_status_code,
-                   int expected_error_code,
-                   const std::string& expected_response) {
-    net::TestDelegate delegate;
-    request_ = url_request_context_.CreateRequest(
-        url, net::DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
-    request_->set_method(method);
-    if (!extra_headers.IsEmpty())
-      request_->SetExtraRequestHeaders(extra_headers);
-    request_->Start();
-
-    base::RunLoop().RunUntilIdle();
-
-    // Verify response.
-    if (expected_error_code)
-      EXPECT_EQ(expected_error_code, request_->status().error());
-    else
-      EXPECT_TRUE(request_->status().is_success());
-    ASSERT_TRUE(request_->response_headers());
-    EXPECT_EQ(expected_status_code,
-              request_->response_headers()->response_code());
-    EXPECT_EQ(expected_response, delegate.data_received());
-  }
-
- protected:
-  base::test::ScopedTaskEnvironment task_environment_{
-      base::test::ScopedTaskEnvironment::MainThreadType::IO};
-  std::unique_ptr<StreamRegistry> registry_;
-
-  net::URLRequestContext url_request_context_;
-  net::URLRequestJobFactoryImpl url_request_job_factory_;
-  std::unique_ptr<net::URLRequest> request_;
-};
-
-TEST_F(StreamURLRequestJobTest, TestGetSimpleDataRequest) {
-  scoped_refptr<Stream> stream(
-      new Stream(registry_.get(), nullptr, kStreamURL));
-  stream->OnResponseStarted(*BuildResponseInfo());
-
-  scoped_refptr<net::StringIOBuffer> buffer =
-      base::MakeRefCounted<net::StringIOBuffer>(kTestData1);
-
-  stream->AddData(buffer, buffer->size());
-  stream->Finalize(net::OK);
-
-  TestSuccessRequest(kStreamURL, kTestData1);
-}
-
-TEST_F(StreamURLRequestJobTest, TestGetLargeStreamRequest) {
-  scoped_refptr<Stream> stream(
-      new Stream(registry_.get(), nullptr, kStreamURL));
-  stream->OnResponseStarted(*BuildResponseInfo());
-
-  std::string large_data;
-  large_data.reserve(kBufferSize * 5);
-  for (int i = 0; i < kBufferSize * 5; ++i)
-    large_data.append(1, static_cast<char>(i % 256));
-
-  scoped_refptr<net::StringIOBuffer> buffer =
-      base::MakeRefCounted<net::StringIOBuffer>(large_data);
-
-  stream->AddData(buffer, buffer->size());
-  stream->Finalize(net::OK);
-  TestSuccessRequest(kStreamURL, large_data);
-}
-
-TEST_F(StreamURLRequestJobTest, TestGetNonExistentStreamRequest) {
-  net::TestDelegate delegate;
-  request_ = url_request_context_.CreateRequest(
-      kStreamURL, net::DEFAULT_PRIORITY, &delegate,
-      TRAFFIC_ANNOTATION_FOR_TESTS);
-  request_->set_method("GET");
-  request_->Start();
-
-  base::RunLoop().RunUntilIdle();
-
-  // Verify response.
-  EXPECT_FALSE(request_->status().is_success());
-}
-
-TEST_F(StreamURLRequestJobTest, TestRangeDataRequest) {
-  scoped_refptr<Stream> stream(
-      new Stream(registry_.get(), nullptr, kStreamURL));
-  stream->OnResponseStarted(*BuildResponseInfo());
-
-  scoped_refptr<net::StringIOBuffer> buffer =
-      base::MakeRefCounted<net::StringIOBuffer>(kTestData2);
-
-  stream->AddData(buffer, buffer->size());
-  stream->Finalize(net::OK);
-
-  net::HttpRequestHeaders extra_headers;
-  extra_headers.SetHeader(net::HttpRequestHeaders::kRange,
-                          net::HttpByteRange::Bounded(0, 3).GetHeaderValue());
-  TestRequest("GET", kStreamURL, extra_headers,
-              200, net::OK, std::string(kTestData2, 4));
-}
-
-TEST_F(StreamURLRequestJobTest, TestInvalidRangeDataRequest) {
-  scoped_refptr<Stream> stream(
-      new Stream(registry_.get(), nullptr, kStreamURL));
-  stream->OnResponseStarted(*BuildResponseInfo());
-  scoped_refptr<net::StringIOBuffer> buffer =
-      base::MakeRefCounted<net::StringIOBuffer>(kTestData2);
-
-  stream->AddData(buffer, buffer->size());
-  stream->Finalize(net::OK);
-
-  net::HttpRequestHeaders extra_headers;
-  extra_headers.SetHeader(net::HttpRequestHeaders::kRange,
-                          net::HttpByteRange::Bounded(1, 3).GetHeaderValue());
-  TestRequest("GET", kStreamURL, extra_headers, 405,
-              net::ERR_METHOD_NOT_SUPPORTED, std::string());
-}
-
-}  // namespace content
diff --git a/content/browser/streams/stream_write_observer.h b/content/browser/streams/stream_write_observer.h
deleted file mode 100644
index deab7ad..0000000
--- a/content/browser/streams/stream_write_observer.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_STREAMS_STREAM_WRITE_OBSERVER_H_
-#define CONTENT_BROWSER_STREAMS_STREAM_WRITE_OBSERVER_H_
-
-namespace content {
-
-class Stream;
-
-class StreamWriteObserver {
- public:
-  // Sent when space becomes available in the stream, and the source should
-  // resume writing.
-  virtual void OnSpaceAvailable(Stream* stream) = 0;
-
-  // Sent when the stream is closed, and the writer should stop sending data.
-  virtual void OnClose(Stream* stream) = 0;
-
- protected:
-  virtual ~StreamWriteObserver() {}
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_STREAMS_STREAM_WRITE_OBSERVER_H_
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index f88cff5..9f68ff3 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -154,6 +154,7 @@
 #include "third_party/blink/public/common/frame/sandbox_flags.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
 #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/platform/web_security_style.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/accessibility/ax_tree_combiner.h"
@@ -3090,9 +3091,10 @@
   if (delegate_) {
     delegate_->RequestMediaAccessPermission(this, request, std::move(callback));
   } else {
-    std::move(callback).Run(blink::MediaStreamDevices(),
-                            blink::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN,
-                            std::unique_ptr<MediaStreamUI>());
+    std::move(callback).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN,
+        std::unique_ptr<MediaStreamUI>());
   }
 }
 
diff --git a/content/browser/worker_host/shared_worker_service_impl_unittest.cc b/content/browser/worker_host/shared_worker_service_impl_unittest.cc
index 71d5077..84513ca4 100644
--- a/content/browser/worker_host/shared_worker_service_impl_unittest.cc
+++ b/content/browser/worker_host/shared_worker_service_impl_unittest.cc
@@ -685,7 +685,9 @@
   EXPECT_TRUE(worker.CheckReceivedTerminate());
 }
 
-TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_PendingCase_URLMismatch) {
+// TODO(https://crbug.com/968971): Flaky fails on all bots.
+TEST_F(SharedWorkerServiceImplTest,
+       DISABLED_CreateWorkerTest_PendingCase_URLMismatch) {
   const GURL kUrl0("http://example.com/w0.js");
   const GURL kUrl1("http://example.com/w1.js");
   const char kName[] = "name";
@@ -776,7 +778,9 @@
   EXPECT_TRUE(worker1.CheckReceivedTerminate());
 }
 
-TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_PendingCase_NameMismatch) {
+// TODO(https://crbug.com/968971): Flaky fails on all bots.
+TEST_F(SharedWorkerServiceImplTest,
+       DISABLED_CreateWorkerTest_PendingCase_NameMismatch) {
   const GURL kUrl("http://example.com/w.js");
   const char kName0[] = "name0";
   const char kName1[] = "name1";
diff --git a/content/child/memory/OWNERS b/content/child/memory/OWNERS
deleted file mode 100644
index 04e3019f..0000000
--- a/content/child/memory/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-bashi@chromium.org
-chrisha@chromium.org
-haraken@chromium.org
-
-# TEAM: platform-architecture-dev@chromium.org
-# COMPONENT: Blink>MemoryAllocator
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 96209da..ffddd10a 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -305,9 +305,6 @@
     "storage_usage_info.h",
     "stored_payment_app.cc",
     "stored_payment_app.h",
-    "stream_handle.h",
-    "stream_info.cc",
-    "stream_info.h",
     "swap_metrics_driver.h",
     "touch_selection_controller_client_manager.h",
     "trace_uploader.h",
diff --git a/content/public/browser/media_stream_request.h b/content/public/browser/media_stream_request.h
index bc974b313..f086caa 100644
--- a/content/public/browser/media_stream_request.h
+++ b/content/public/browser/media_stream_request.h
@@ -10,6 +10,7 @@
 #include "base/callback_forward.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "ui/gfx/native_widget_types.h"
 #include "url/gurl.h"
 
@@ -99,7 +100,7 @@
 // Callback used return results of media access requests.
 using MediaResponseCallback =
     base::OnceCallback<void(const blink::MediaStreamDevices& devices,
-                            blink::MediaStreamRequestResult result,
+                            blink::mojom::MediaStreamRequestResult result,
                             std::unique_ptr<MediaStreamUI> ui)>;
 }  // namespace content
 
diff --git a/content/public/browser/origin_policy_commands.h b/content/public/browser/origin_policy_commands.h
index 358f1f7..7533193 100644
--- a/content/public/browser/origin_policy_commands.h
+++ b/content/public/browser/origin_policy_commands.h
@@ -11,11 +11,14 @@
 
 namespace content {
 
+class BrowserContext;
+
 // Instruct the Origin Policy throttle to disregard errors for the given URL.
 //
 // Intended use: This should be called by the browser when the user selects
 // "proceed" on the security interstitial page for the given URL.
-CONTENT_EXPORT void OriginPolicyAddExceptionFor(const GURL& url);
+CONTENT_EXPORT void OriginPolicyAddExceptionFor(BrowserContext* browser_context,
+                                                const GURL& url);
 
 }  // namespace content
 
diff --git a/content/public/browser/resource_dispatcher_host_delegate.cc b/content/public/browser/resource_dispatcher_host_delegate.cc
index 8607e47..95c5d64 100644
--- a/content/public/browser/resource_dispatcher_host_delegate.cc
+++ b/content/public/browser/resource_dispatcher_host_delegate.cc
@@ -6,7 +6,6 @@
 
 #include "content/public/browser/navigation_data.h"
 #include "content/public/browser/resource_request_info.h"
-#include "content/public/browser/stream_info.h"
 
 namespace content {
 
@@ -27,18 +26,6 @@
     bool is_new_request,
     std::vector<std::unique_ptr<ResourceThrottle>>* throttles) {}
 
-bool ResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
-    net::URLRequest* request,
-    const std::string& mime_type,
-    GURL* origin,
-    std::string* payload) {
-  return false;
-}
-
-void ResourceDispatcherHostDelegate::OnStreamCreated(
-    net::URLRequest* request,
-    std::unique_ptr<content::StreamInfo> stream) {}
-
 void ResourceDispatcherHostDelegate::OnResponseStarted(
     net::URLRequest* request,
     ResourceContext* resource_context,
diff --git a/content/public/browser/resource_dispatcher_host_delegate.h b/content/public/browser/resource_dispatcher_host_delegate.h
index 5ee1441f..aed1343 100644
--- a/content/public/browser/resource_dispatcher_host_delegate.h
+++ b/content/public/browser/resource_dispatcher_host_delegate.h
@@ -28,7 +28,6 @@
 class NavigationData;
 class ResourceContext;
 class ResourceThrottle;
-struct StreamInfo;
 
 // Interface that the embedder provides to ResourceDispatcherHost to allow
 // observing and modifying requests.
@@ -58,26 +57,6 @@
       bool is_new_request,
       std::vector<std::unique_ptr<ResourceThrottle>>* throttles);
 
-  // Returns true and sets |origin| if a Stream should be created for the
-  // resource. If true is returned, a new Stream will be created and
-  // OnStreamCreated() will be called with a StreamHandle instance for the
-  // Stream. The handle contains the URL for reading the Stream etc. The
-  // Stream's origin will be set to |origin|.
-  //
-  // If the stream will be rendered in a BrowserPlugin, |payload| will contain
-  // the data that should be given to the old ResourceHandler to forward to the
-  // renderer process.
-  virtual bool ShouldInterceptResourceAsStream(
-      net::URLRequest* request,
-      const std::string& mime_type,
-      GURL* origin,
-      std::string* payload);
-
-  // Informs the delegate that a Stream was created. The Stream can be read from
-  // the blob URL of the Stream, but can only be read once.
-  virtual void OnStreamCreated(net::URLRequest* request,
-                               std::unique_ptr<content::StreamInfo> stream);
-
   // Informs the delegate that a response has started.
   virtual void OnResponseStarted(net::URLRequest* request,
                                  ResourceContext* resource_context,
diff --git a/content/public/browser/stream_handle.h b/content/public/browser/stream_handle.h
deleted file mode 100644
index 7a8fb32..0000000
--- a/content/public/browser/stream_handle.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_PUBLIC_BROWSER_STREAM_HANDLE_H_
-#define CONTENT_PUBLIC_BROWSER_STREAM_HANDLE_H_
-
-#include "base/callback_forward.h"
-#include "content/common/content_export.h"
-#include "url/gurl.h"
-
-namespace content {
-
-// A handle to a Stream. When the handle is destroyed, the stream is released
-// and the URL is invalidated.
-class CONTENT_EXPORT StreamHandle {
- public:
-  virtual ~StreamHandle() {}
-
-  // Gets the URL the stream can be read from.
-  virtual const GURL& GetURL() = 0;
-
-  // Add a callback which will be called when the Stream is closed.
-  virtual void AddCloseListener(const base::Closure& callback) = 0;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_PUBLIC_BROWSER_STREAM_HANDLE_H_
diff --git a/content/public/browser/stream_info.cc b/content/public/browser/stream_info.cc
deleted file mode 100644
index 5472b98..0000000
--- a/content/public/browser/stream_info.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/public/browser/stream_info.h"
-
-#include "content/public/browser/stream_handle.h"
-#include "net/http/http_response_headers.h"
-
-namespace content {
-
-StreamInfo::StreamInfo() {}
-
-StreamInfo::~StreamInfo() {}
-
-}  // namespace content
diff --git a/content/public/browser/stream_info.h b/content/public/browser/stream_info.h
deleted file mode 100644
index 306d49b..0000000
--- a/content/public/browser/stream_info.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_PUBLIC_BROWSER_STREAM_INFO_H_
-#define CONTENT_PUBLIC_BROWSER_STREAM_INFO_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "content/common/content_export.h"
-#include "url/gurl.h"
-
-namespace net {
-class HttpResponseHeaders;
-}
-
-namespace content {
-
-class StreamHandle;
-
-// A convenience structure for passing around both a StreamHandle and associated
-// metadata. The intent is so, when passing the stream's URL to a child process,
-// the handle can be retained as long as it is needed while the rest of the
-// StreamInfo is released.
-struct CONTENT_EXPORT StreamInfo {
-  StreamInfo();
-  ~StreamInfo();
-
-  // The handle to the stream itself.
-  std::unique_ptr<StreamHandle> handle;
-
-  // The original URL being redirected to this stream.
-  GURL original_url;
-
-  // The MIME type associated with this stream.
-  std::string mime_type;
-
-  // The HTTP response headers associated with this stream.
-  scoped_refptr<net::HttpResponseHeaders> response_headers;
-
-  DISALLOW_COPY_AND_ASSIGN(StreamInfo);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_PUBLIC_BROWSER_STREAM_INFO_H_
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index 848f890..5f832d9 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -19,6 +19,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/bindings_policy.h"
 #include "content/public/common/url_constants.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace content {
@@ -192,7 +193,7 @@
   LOG(ERROR) << "WebContentsDelegate::RequestMediaAccessPermission: "
              << "Not supported.";
   std::move(callback).Run(blink::MediaStreamDevices(),
-                          blink::MEDIA_DEVICE_NOT_SUPPORTED,
+                          blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED,
                           std::unique_ptr<content::MediaStreamUI>());
 }
 
diff --git a/content/public/test/browser_side_navigation_test_utils.cc b/content/public/test/browser_side_navigation_test_utils.cc
index 07b4c7c..112e36ce 100644
--- a/content/public/test/browser_side_navigation_test_utils.cc
+++ b/content/public/test/browser_side_navigation_test_utils.cc
@@ -8,10 +8,7 @@
 #include "base/guid.h"
 #include "base/lazy_instance.h"
 #include "base/macros.h"
-#include "content/browser/streams/stream.h"
-#include "content/browser/streams/stream_registry.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/stream_handle.h"
 #include "content/public/common/content_switches.h"
 #include "content/test/test_navigation_url_loader_factory.h"
 #include "net/base/net_errors.h"
@@ -28,15 +25,11 @@
 class BrowserSideNavigationTestUtils {
  public:
   BrowserSideNavigationTestUtils()
-      : stream_registry_(new StreamRegistry),
-        loader_factory_(new TestNavigationURLLoaderFactory) {
-  }
+      : loader_factory_(new TestNavigationURLLoaderFactory) {}
 
   ~BrowserSideNavigationTestUtils() {}
-  StreamRegistry* stream_registry() { return stream_registry_.get();}
 
  private:
-  std::unique_ptr<StreamRegistry> stream_registry_;
   std::unique_ptr<TestNavigationURLLoaderFactory> loader_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(BrowserSideNavigationTestUtils);
@@ -58,13 +51,4 @@
   browser_side_navigation_test_utils.Get().reset(nullptr);
 }
 
-std::unique_ptr<StreamHandle> MakeEmptyStream() {
-  GURL url(std::string(url::kBlobScheme) + "://" + base::GenerateGUID());
-  StreamRegistry* stream_registry =
-      browser_side_navigation_test_utils.Get()->stream_registry();
-  scoped_refptr<Stream> stream(new Stream(stream_registry, nullptr, url));
-  stream->Finalize(net::OK);
-  return stream->CreateHandle();
-}
-
 }  // namespace content
diff --git a/content/public/test/browser_side_navigation_test_utils.h b/content/public/test/browser_side_navigation_test_utils.h
index ce16522..ba1630c 100644
--- a/content/public/test/browser_side_navigation_test_utils.h
+++ b/content/public/test/browser_side_navigation_test_utils.h
@@ -11,8 +11,6 @@
 
 namespace content {
 
-class StreamHandle;
-
 // Initializes the browser side navigation test utils. Following this call, all
 // NavigationURLLoader objects created will be TestNavigationURLLoaders instead
 // of NavigationURLloaderImpls. This should be called before any call in the UI
@@ -23,10 +21,6 @@
 // Tears down the browser side navigation test utils.
 void BrowserSideNavigationTearDown();
 
-// Returns an empty stream. Used when faking a navigation commit notification
-// from the IO thread with a TestNavigationURLLoader.
-std::unique_ptr<StreamHandle> MakeEmptyStream();
-
 }  // namespace content
 
 #endif  // CONTENT_PUBLIC_TEST_BROWSER_SIDE_NAVIGATION_TEST_UTILS_H_
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.cc b/content/renderer/loader/web_worker_fetch_context_impl.cc
index 5395cc4..1c845f8 100644
--- a/content/renderer/loader/web_worker_fetch_context_impl.cc
+++ b/content/renderer/loader/web_worker_fetch_context_impl.cc
@@ -242,9 +242,11 @@
   terminate_sync_load_event_ = terminate_sync_load_event;
 }
 
-scoped_refptr<blink::WebWorkerFetchContext>
-WebWorkerFetchContextImpl::CloneForNestedWorker(
+scoped_refptr<WebWorkerFetchContextImpl>
+WebWorkerFetchContextImpl::CloneForNestedWorkerDeprecated(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  DCHECK(!blink::features::IsPlzDedicatedWorkerEnabled());
+
   blink::mojom::ServiceWorkerWorkerClientRequest service_worker_client_request;
   blink::mojom::ServiceWorkerWorkerClientRegistryPtrInfo
       service_worker_worker_client_registry_ptr_info;
@@ -264,28 +266,52 @@
         mojo::MakeRequest(&host_ptr_info));
   }
 
-  blink::mojom::RendererPreferenceWatcherPtr preference_watcher;
-  auto new_context = base::AdoptRef(new WebWorkerFetchContextImpl(
-      renderer_preferences_, mojo::MakeRequest(&preference_watcher),
-      std::move(service_worker_client_request),
-      std::move(service_worker_worker_client_registry_ptr_info),
-      std::move(host_ptr_info), loader_factory_->Clone(),
-      fallback_factory_->Clone(),
-      throttle_provider_ ? throttle_provider_->Clone() : nullptr,
-      websocket_handshake_throttle_provider_
-          ? websocket_handshake_throttle_provider_->Clone(
-                std::move(task_runner))
-          : nullptr,
-      thread_safe_sender_.get(), service_manager_connection_->Clone()));
+  scoped_refptr<WebWorkerFetchContextImpl> new_context =
+      CloneForNestedWorkerInternal(
+          std::move(service_worker_client_request),
+          std::move(service_worker_worker_client_registry_ptr_info),
+          std::move(host_ptr_info), loader_factory_->Clone(),
+          fallback_factory_->Clone(), std::move(task_runner));
   new_context->is_controlled_by_service_worker_ =
       is_controlled_by_service_worker_;
-  new_context->is_on_sub_frame_ = is_on_sub_frame_;
-  new_context->ancestor_frame_id_ = ancestor_frame_id_;
-  new_context->frame_request_blocker_ = frame_request_blocker_;
-  new_context->appcache_host_id_ = appcache_host_id_;
-  new_context->top_frame_origin_ = top_frame_origin_;
 
-  child_preference_watchers_.AddPtr(std::move(preference_watcher));
+  return new_context;
+}
+
+scoped_refptr<WebWorkerFetchContextImpl>
+WebWorkerFetchContextImpl::CloneForNestedWorker(
+    ServiceWorkerProviderContext* service_worker_provider_context,
+    std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info,
+    std::unique_ptr<network::SharedURLLoaderFactoryInfo> fallback_factory_info,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  DCHECK(blink::features::IsPlzDedicatedWorkerEnabled());
+  DCHECK(service_worker_provider_context);
+  DCHECK(loader_factory_info);
+  DCHECK(fallback_factory_info);
+  DCHECK(task_runner);
+
+  blink::mojom::ServiceWorkerWorkerClientRegistryPtrInfo
+      service_worker_worker_client_registry_ptr_info;
+  service_worker_provider_context->CloneWorkerClientRegistry(
+      mojo::MakeRequest(&service_worker_worker_client_registry_ptr_info));
+
+  blink::mojom::ServiceWorkerWorkerClientPtr worker_client_ptr;
+  blink::mojom::ServiceWorkerWorkerClientRequest service_worker_client_request =
+      mojo::MakeRequest(&worker_client_ptr);
+  service_worker_provider_context->RegisterWorkerClient(
+      std::move(worker_client_ptr));
+
+  blink::mojom::ServiceWorkerContainerHostPtrInfo container_host_ptr_info =
+      service_worker_provider_context->CloneContainerHostPtrInfo();
+
+  scoped_refptr<WebWorkerFetchContextImpl> new_context =
+      CloneForNestedWorkerInternal(
+          std::move(service_worker_client_request),
+          std::move(service_worker_worker_client_registry_ptr_info),
+          std::move(container_host_ptr_info), std::move(loader_factory_info),
+          std::move(fallback_factory_info), std::move(task_runner));
+  new_context->is_controlled_by_service_worker_ =
+      service_worker_provider_context->IsControlledByServiceWorker();
 
   return new_context;
 }
@@ -508,6 +534,38 @@
   ResetServiceWorkerURLLoaderFactory();
 }
 
+scoped_refptr<WebWorkerFetchContextImpl>
+WebWorkerFetchContextImpl::CloneForNestedWorkerInternal(
+    blink::mojom::ServiceWorkerWorkerClientRequest
+        service_worker_client_request,
+    blink::mojom::ServiceWorkerWorkerClientRegistryPtrInfo
+        service_worker_worker_client_registry_ptr_info,
+    blink::mojom::ServiceWorkerContainerHostPtrInfo container_host_ptr_info,
+    std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info,
+    std::unique_ptr<network::SharedURLLoaderFactoryInfo> fallback_factory_info,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  blink::mojom::RendererPreferenceWatcherPtr preference_watcher;
+  auto new_context = base::AdoptRef(new WebWorkerFetchContextImpl(
+      renderer_preferences_, mojo::MakeRequest(&preference_watcher),
+      std::move(service_worker_client_request),
+      std::move(service_worker_worker_client_registry_ptr_info),
+      std::move(container_host_ptr_info), std::move(loader_factory_info),
+      std::move(fallback_factory_info),
+      throttle_provider_ ? throttle_provider_->Clone() : nullptr,
+      websocket_handshake_throttle_provider_
+          ? websocket_handshake_throttle_provider_->Clone(
+                std::move(task_runner))
+          : nullptr,
+      thread_safe_sender_.get(), service_manager_connection_->Clone()));
+  new_context->is_on_sub_frame_ = is_on_sub_frame_;
+  new_context->ancestor_frame_id_ = ancestor_frame_id_;
+  new_context->frame_request_blocker_ = frame_request_blocker_;
+  new_context->appcache_host_id_ = appcache_host_id_;
+  new_context->top_frame_origin_ = top_frame_origin_;
+  child_preference_watchers_.AddPtr(std::move(preference_watcher));
+  return new_context;
+}
+
 bool WebWorkerFetchContextImpl::Send(IPC::Message* message) {
   return thread_safe_sender_->Send(message);
 }
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.h b/content/renderer/loader/web_worker_fetch_context_impl.h
index 3fe0c83..7972502 100644
--- a/content/renderer/loader/web_worker_fetch_context_impl.h
+++ b/content/renderer/loader/web_worker_fetch_context_impl.h
@@ -77,9 +77,22 @@
       std::unique_ptr<network::SharedURLLoaderFactoryInfo>
           fallback_factory_info);
 
+  // Clones this fetch context for a nested worker.
+  // For non-PlzDedicatedWorker. This will be removed once PlzDedicatedWorker is
+  // enabled by default.
+  scoped_refptr<WebWorkerFetchContextImpl> CloneForNestedWorkerDeprecated(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  // For PlzDedicatedWorker. The cloned fetch context does not inherit some
+  // fields (e.g., ServiceWorkerProviderContext) from this fetch context, and
+  // instead that takes values passed from the browser process.
+  scoped_refptr<WebWorkerFetchContextImpl> CloneForNestedWorker(
+      ServiceWorkerProviderContext* service_worker_provider_context,
+      std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info,
+      std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+          fallback_factory_info,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
   // blink::WebWorkerFetchContext implementation:
-  scoped_refptr<blink::WebWorkerFetchContext> CloneForNestedWorker(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
   void SetTerminateSyncLoadEvent(base::WaitableEvent*) override;
   void InitializeOnWorkerThread(blink::AcceptLanguagesWatcher*) override;
   blink::WebURLLoaderFactory* GetURLLoaderFactory() override;
@@ -166,6 +179,17 @@
 
   ~WebWorkerFetchContextImpl() override;
 
+  scoped_refptr<WebWorkerFetchContextImpl> CloneForNestedWorkerInternal(
+      blink::mojom::ServiceWorkerWorkerClientRequest
+          service_worker_client_request,
+      blink::mojom::ServiceWorkerWorkerClientRegistryPtrInfo
+          service_worker_worker_client_registry_ptr_info,
+      blink::mojom::ServiceWorkerContainerHostPtrInfo container_host_ptr_info,
+      std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info,
+      std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+          fallback_factory_info,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
   bool Send(IPC::Message* message);
 
   // Resets the service worker url loader factory of a URLLoaderFactoryImpl
diff --git a/content/renderer/media/stream/local_media_stream_audio_source.cc b/content/renderer/media/stream/local_media_stream_audio_source.cc
index 9f7325a0..56c1b64 100644
--- a/content/renderer/media/stream/local_media_stream_audio_source.cc
+++ b/content/renderer/media/stream/local_media_stream_audio_source.cc
@@ -9,6 +9,7 @@
 #include "content/renderer/media/audio/audio_device_factory.h"
 #include "content/renderer/media/webrtc_logging.h"
 #include "content/renderer/render_frame_impl.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 namespace content {
 
@@ -105,7 +106,7 @@
 }
 
 void LocalMediaStreamAudioSource::OnCaptureStarted() {
-  started_callback_.Run(this, blink::MEDIA_DEVICE_OK, "");
+  started_callback_.Run(this, blink::mojom::MediaStreamRequestResult::OK, "");
 }
 
 void LocalMediaStreamAudioSource::Capture(const media::AudioBus* audio_bus,
diff --git a/content/renderer/media/stream/media_stream_video_source_unittest.cc b/content/renderer/media/stream/media_stream_video_source_unittest.cc
index d981d7c..d618bff 100644
--- a/content/renderer/media/stream/media_stream_video_source_unittest.cc
+++ b/content/renderer/media/stream/media_stream_video_source_unittest.cc
@@ -19,6 +19,7 @@
 #include "media/base/limits.h"
 #include "media/base/video_frame.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/public/web/modules/mediastream/video_track_adapter_settings.h"
@@ -38,7 +39,7 @@
         child_process_(new ChildProcess()),
         number_of_successful_constraints_applied_(0),
         number_of_failed_constraints_applied_(0),
-        result_(blink::MEDIA_DEVICE_OK),
+        result_(blink::mojom::MediaStreamRequestResult::OK),
         result_name_(""),
         mock_source_(new MockMediaStreamVideoSource(
             media::VideoCaptureFormat(gfx::Size(1280, 720),
@@ -124,7 +125,7 @@
     return number_of_failed_constraints_applied_;
   }
 
-  blink::MediaStreamRequestResult error_type() const { return result_; }
+  blink::mojom::MediaStreamRequestResult error_type() const { return result_; }
   blink::WebString error_name() const { return result_name_; }
 
   MockMediaStreamVideoSource* mock_source() { return mock_source_; }
@@ -236,11 +237,11 @@
 
  private:
   void OnConstraintsApplied(blink::WebPlatformMediaStreamSource* source,
-                            blink::MediaStreamRequestResult result,
+                            blink::mojom::MediaStreamRequestResult result,
                             const blink::WebString& result_name) {
     ASSERT_EQ(source, web_source().GetPlatformSource());
 
-    if (result == blink::MEDIA_DEVICE_OK) {
+    if (result == blink::mojom::MediaStreamRequestResult::OK) {
       ++number_of_successful_constraints_applied_;
     } else {
       result_ = result;
@@ -259,7 +260,7 @@
   blink::WebMediaStreamTrack track_to_release_;
   int number_of_successful_constraints_applied_;
   int number_of_failed_constraints_applied_;
-  blink::MediaStreamRequestResult result_;
+  blink::mojom::MediaStreamRequestResult result_;
   blink::WebString result_name_;
   blink::WebMediaStreamSource web_source_;
   // |mock_source_| is owned by |web_source_|.
diff --git a/content/renderer/media/stream/mock_media_stream_video_source.cc b/content/renderer/media/stream/mock_media_stream_video_source.cc
index 09ab6a2..6032421 100644
--- a/content/renderer/media/stream/mock_media_stream_video_source.cc
+++ b/content/renderer/media/stream/mock_media_stream_video_source.cc
@@ -8,6 +8,7 @@
 #include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 namespace content {
 
@@ -37,13 +38,14 @@
 void MockMediaStreamVideoSource::StartMockedSource() {
   DCHECK(attempted_to_start_);
   attempted_to_start_ = false;
-  OnStartDone(blink::MEDIA_DEVICE_OK);
+  OnStartDone(blink::mojom::MediaStreamRequestResult::OK);
 }
 
 void MockMediaStreamVideoSource::FailToStartMockedSource() {
   DCHECK(attempted_to_start_);
   attempted_to_start_ = false;
-  OnStartDone(blink::MEDIA_DEVICE_TRACK_START_FAILURE_VIDEO);
+  OnStartDone(
+      blink::mojom::MediaStreamRequestResult::TRACK_START_FAILURE_VIDEO);
 }
 
 void MockMediaStreamVideoSource::RequestRefreshFrame() {
diff --git a/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc b/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc
index af68f05..92dea2d7 100644
--- a/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc
+++ b/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc
@@ -5,6 +5,7 @@
 #include "content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.h"
 
 #include "base/strings/string_number_conversions.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 namespace content {
 
@@ -56,7 +57,7 @@
   if (do_not_run_cb_) {
     generate_stream_cb_ = std::move(callback);
   } else {
-    std::move(callback).Run(blink::MEDIA_DEVICE_OK,
+    std::move(callback).Run(blink::mojom::MediaStreamRequestResult::OK,
                             "dummy" + base::NumberToString(request_id_),
                             audio_devices_, video_devices_);
   }
diff --git a/content/renderer/media/stream/processed_local_audio_source.cc b/content/renderer/media/stream/processed_local_audio_source.cc
index d87321d..71bf6dfa 100644
--- a/content/renderer/media/stream/processed_local_audio_source.cc
+++ b/content/renderer/media/stream/processed_local_audio_source.cc
@@ -21,6 +21,7 @@
 #include "media/base/channel_layout.h"
 #include "media/base/sample_rates.h"
 #include "media/webrtc/webrtc_switches.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_processor_options.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h"
 #include "third_party/webrtc/media/base/media_channel.h"
@@ -357,7 +358,8 @@
 }
 
 void ProcessedLocalAudioSource::OnCaptureStarted() {
-  std::move(started_callback_).Run(this, blink::MEDIA_DEVICE_OK, "");
+  std::move(started_callback_)
+      .Run(this, blink::mojom::MediaStreamRequestResult::OK, "");
 }
 
 void ProcessedLocalAudioSource::Capture(const media::AudioBus* audio_bus,
diff --git a/content/renderer/media/stream/user_media_client_impl_unittest.cc b/content/renderer/media/stream/user_media_client_impl_unittest.cc
index a76827d..c2a41bb 100644
--- a/content/renderer/media/stream/user_media_client_impl_unittest.cc
+++ b/content/renderer/media/stream/user_media_client_impl_unittest.cc
@@ -26,6 +26,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/mediastream/media_devices.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_processor_options.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_track.h"
@@ -343,7 +344,9 @@
     return VideoCaptureSettingsForTesting();
   }
 
-  blink::MediaStreamRequestResult error_reason() const { return result_; }
+  blink::mojom::MediaStreamRequestResult error_reason() const {
+    return result_;
+  }
   blink::WebString constraint_name() const { return constraint_name_; }
 
   // UserMediaProcessor overrides.
@@ -403,7 +406,7 @@
   }
 
   void GetUserMediaRequestFailed(
-      blink::MediaStreamRequestResult result,
+      blink::mojom::MediaStreamRequestResult result,
       const blink::WebString& constraint_name) override {
     last_generated_stream_.Reset();
     *state_ = REQUEST_FAILED;
@@ -415,7 +418,8 @@
   static void SignalSourceReady(
       blink::WebPlatformMediaStreamSource::ConstraintsOnceCallback source_ready,
       blink::WebPlatformMediaStreamSource* source) {
-    std::move(source_ready).Run(source, blink::MEDIA_DEVICE_OK, "");
+    std::move(source_ready)
+        .Run(source, blink::mojom::MediaStreamRequestResult::OK, "");
   }
 
   PeerConnectionDependencyFactory* factory_;
@@ -424,7 +428,8 @@
   MockLocalMediaStreamAudioSource* local_audio_source_ = nullptr;
   bool create_source_that_fails_ = false;
   blink::WebMediaStream last_generated_stream_;
-  blink::MediaStreamRequestResult result_ = blink::NUM_MEDIA_REQUEST_RESULTS;
+  blink::mojom::MediaStreamRequestResult result_ =
+      blink::mojom::MediaStreamRequestResult::NUM_MEDIA_REQUEST_RESULTS;
   blink::WebString constraint_name_;
   RequestState* state_;
 };
@@ -804,7 +809,7 @@
   FailToStartMockedVideoSource();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(REQUEST_FAILED, request_state());
-  EXPECT_EQ(blink::MEDIA_DEVICE_TRACK_START_FAILURE_VIDEO,
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::TRACK_START_FAILURE_VIDEO,
             user_media_processor_->error_reason());
   blink::WebHeap::CollectAllGarbageForTesting();
   EXPECT_EQ(1, mock_dispatcher_host_.request_stream_counter());
@@ -819,7 +824,7 @@
   StartMockedVideoSource();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(REQUEST_FAILED, request_state());
-  EXPECT_EQ(blink::MEDIA_DEVICE_TRACK_START_FAILURE_AUDIO,
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::TRACK_START_FAILURE_AUDIO,
             user_media_processor_->error_reason());
   blink::WebHeap::CollectAllGarbageForTesting();
   EXPECT_EQ(1, mock_dispatcher_host_.request_stream_counter());
diff --git a/content/renderer/media/stream/user_media_processor.cc b/content/renderer/media/stream/user_media_processor.cc
index fd70cdb2..feea0e1 100644
--- a/content/renderer/media/stream/user_media_processor.cc
+++ b/content/renderer/media/stream/user_media_processor.cc
@@ -54,11 +54,11 @@
 
 using blink::MediaStreamDevice;
 using blink::MediaStreamDevices;
-using blink::MediaStreamRequestResult;
 using blink::MediaStreamType;
 using blink::StreamControls;
 using blink::TrackControls;
 using blink::WebMediaStreamSource;
+using blink::mojom::MediaStreamRequestResult;
 using EchoCancellationType =
     blink::AudioProcessingProperties::EchoCancellationType;
 
@@ -339,7 +339,7 @@
   StreamControls stream_controls_;
   const url::Origin security_origin_;
   ResourcesReady ready_callback_;
-  MediaStreamRequestResult request_result_ = blink::MEDIA_DEVICE_OK;
+  MediaStreamRequestResult request_result_ = MediaStreamRequestResult::OK;
   blink::WebString request_result_name_;
   // Sources used in this request.
   std::vector<blink::WebMediaStreamSource> sources_;
@@ -375,8 +375,9 @@
   bool connected = native_source->ConnectToTrack(track);
   if (!is_pending) {
     OnTrackStarted(native_source,
-                   connected ? blink::MEDIA_DEVICE_OK
-                             : blink::MEDIA_DEVICE_TRACK_START_FAILURE_AUDIO,
+                   connected
+                       ? MediaStreamRequestResult::OK
+                       : MediaStreamRequestResult::TRACK_START_FAILURE_AUDIO,
                    "");
   }
 }
@@ -418,7 +419,7 @@
   sources_waiting_for_callback_.erase(it);
   // All tracks must be started successfully. Otherwise the request is a
   // failure.
-  if (result != blink::MEDIA_DEVICE_OK) {
+  if (result != MediaStreamRequestResult::OK) {
     request_result_ = result;
     request_result_name_ = result_name;
   }
@@ -513,7 +514,7 @@
                                           .Basic()
                                           .media_stream_source.GetName());
       MediaStreamRequestResult result =
-          blink::MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED;
+          MediaStreamRequestResult::CONSTRAINT_NOT_SATISFIED;
       GetUserMediaRequestFailed(result, failed_constraint_name);
       return;
     }
@@ -571,8 +572,8 @@
         blink::WebString::FromASCII(settings.failed_constraint_name());
     MediaStreamRequestResult result =
         failed_constraint_name.IsEmpty()
-            ? blink::MEDIA_DEVICE_NO_HARDWARE
-            : blink::MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED;
+            ? MediaStreamRequestResult::NO_HARDWARE
+            : MediaStreamRequestResult::CONSTRAINT_NOT_SATISFIED;
     GetUserMediaRequestFailed(result, failed_constraint_name);
     return;
   }
@@ -616,7 +617,7 @@
                                           .Basic()
                                           .media_stream_source.GetName());
       MediaStreamRequestResult result =
-          blink::MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED;
+          MediaStreamRequestResult::CONSTRAINT_NOT_SATISFIED;
       GetUserMediaRequestFailed(result, failed_constraint_name);
       return;
     }
@@ -655,8 +656,8 @@
         blink::WebString::FromASCII(settings.failed_constraint_name());
     MediaStreamRequestResult result =
         failed_constraint_name.IsEmpty()
-            ? blink::MEDIA_DEVICE_NO_HARDWARE
-            : blink::MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED;
+            ? MediaStreamRequestResult::NO_HARDWARE
+            : MediaStreamRequestResult::CONSTRAINT_NOT_SATISFIED;
     GetUserMediaRequestFailed(result, failed_constraint_name);
     return;
   }
@@ -679,8 +680,9 @@
     blink::WebString failed_constraint_name =
         blink::WebString::FromASCII(settings.failed_constraint_name());
     DCHECK(!failed_constraint_name.IsEmpty());
-    GetUserMediaRequestFailed(blink::MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED,
-                              failed_constraint_name);
+    GetUserMediaRequestFailed(
+        MediaStreamRequestResult::CONSTRAINT_NOT_SATISFIED,
+        failed_constraint_name);
     return;
   }
   if (current_request_info_->stream_controls()->video.stream_type !=
@@ -723,7 +725,7 @@
     const MediaStreamDevices& video_devices) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (result != blink::MEDIA_DEVICE_OK) {
+  if (result != MediaStreamRequestResult::OK) {
     OnStreamGenerationFailed(request_id, result);
     return;
   }
@@ -845,7 +847,7 @@
         it->GetPlatformSource();
     if (source_extra_data != source)
       continue;
-    if (result == blink::MEDIA_DEVICE_OK)
+    if (result == MediaStreamRequestResult::OK)
       local_sources_.push_back((*it));
     pending_local_sources_.erase(it);
 
@@ -1159,7 +1161,7 @@
     MediaStreamRequestResult result,
     const blink::WebString& constraint_name) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (result == blink::MEDIA_DEVICE_OK) {
+  if (result == MediaStreamRequestResult::OK) {
     GetUserMediaRequestSucceeded(*request_info->web_stream(),
                                  request_info->web_request());
     GetMediaStreamDispatcherHost()->OnStreamStarted(label);
@@ -1206,7 +1208,7 @@
     const blink::WebMediaStream& stream,
     blink::WebUserMediaRequest web_request) {
   DVLOG(1) << "UserMediaProcessor::DelayedGetUserMediaRequestSucceeded";
-  LogUserMediaRequestResult(blink::MEDIA_DEVICE_OK);
+  blink::LogUserMediaRequestResult(MediaStreamRequestResult::OK);
   DeleteWebRequest(web_request);
   web_request.RequestSucceeded(stream);
 }
@@ -1235,75 +1237,75 @@
     blink::WebUserMediaRequest web_request,
     MediaStreamRequestResult result,
     const blink::WebString& constraint_name) {
-  LogUserMediaRequestResult(result);
+  blink::LogUserMediaRequestResult(result);
   DeleteWebRequest(web_request);
   switch (result) {
-    case blink::MEDIA_DEVICE_OK:
-    case blink::NUM_MEDIA_REQUEST_RESULTS:
+    case MediaStreamRequestResult::OK:
+    case MediaStreamRequestResult::NUM_MEDIA_REQUEST_RESULTS:
       NOTREACHED();
       return;
-    case blink::MEDIA_DEVICE_PERMISSION_DENIED:
+    case MediaStreamRequestResult::PERMISSION_DENIED:
       web_request.RequestFailed(
           blink::WebUserMediaRequest::Error::kPermissionDenied,
           "Permission denied");
       return;
-    case blink::MEDIA_DEVICE_PERMISSION_DISMISSED:
+    case MediaStreamRequestResult::PERMISSION_DISMISSED:
       web_request.RequestFailed(
           blink::WebUserMediaRequest::Error::kPermissionDismissed,
           "Permission dismissed");
       return;
-    case blink::MEDIA_DEVICE_INVALID_STATE:
+    case MediaStreamRequestResult::INVALID_STATE:
       web_request.RequestFailed(
           blink::WebUserMediaRequest::Error::kInvalidState, "Invalid state");
       return;
-    case blink::MEDIA_DEVICE_NO_HARDWARE:
+    case MediaStreamRequestResult::NO_HARDWARE:
       web_request.RequestFailed(
           blink::WebUserMediaRequest::Error::kDevicesNotFound,
           "Requested device not found");
       return;
-    case blink::MEDIA_DEVICE_INVALID_SECURITY_ORIGIN:
+    case MediaStreamRequestResult::INVALID_SECURITY_ORIGIN:
       web_request.RequestFailed(
           blink::WebUserMediaRequest::Error::kSecurityError,
           "Invalid security origin");
       return;
-    case blink::MEDIA_DEVICE_TAB_CAPTURE_FAILURE:
+    case MediaStreamRequestResult::TAB_CAPTURE_FAILURE:
       web_request.RequestFailed(blink::WebUserMediaRequest::Error::kTabCapture,
                                 "Error starting tab capture");
       return;
-    case blink::MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE:
+    case MediaStreamRequestResult::SCREEN_CAPTURE_FAILURE:
       web_request.RequestFailed(
           blink::WebUserMediaRequest::Error::kScreenCapture,
           "Error starting screen capture");
       return;
-    case blink::MEDIA_DEVICE_CAPTURE_FAILURE:
+    case MediaStreamRequestResult::CAPTURE_FAILURE:
       web_request.RequestFailed(blink::WebUserMediaRequest::Error::kCapture,
                                 "Error starting capture");
       return;
-    case blink::MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED:
+    case MediaStreamRequestResult::CONSTRAINT_NOT_SATISFIED:
       web_request.RequestFailedConstraint(constraint_name);
       return;
-    case blink::MEDIA_DEVICE_TRACK_START_FAILURE_AUDIO:
+    case MediaStreamRequestResult::TRACK_START_FAILURE_AUDIO:
       web_request.RequestFailed(blink::WebUserMediaRequest::Error::kTrackStart,
                                 "Could not start audio source");
       return;
-    case blink::MEDIA_DEVICE_TRACK_START_FAILURE_VIDEO:
+    case MediaStreamRequestResult::TRACK_START_FAILURE_VIDEO:
       web_request.RequestFailed(blink::WebUserMediaRequest::Error::kTrackStart,
                                 "Could not start video source");
       return;
-    case blink::MEDIA_DEVICE_NOT_SUPPORTED:
+    case MediaStreamRequestResult::NOT_SUPPORTED:
       web_request.RequestFailed(
           blink::WebUserMediaRequest::Error::kNotSupported, "Not supported");
       return;
-    case blink::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN:
+    case MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN:
       web_request.RequestFailed(
           blink::WebUserMediaRequest::Error::kFailedDueToShutdown,
           "Failed due to shutdown");
       return;
-    case blink::MEDIA_DEVICE_KILL_SWITCH_ON:
+    case MediaStreamRequestResult::KILL_SWITCH_ON:
       web_request.RequestFailed(
           blink::WebUserMediaRequest::Error::kKillSwitchOn);
       return;
-    case blink::MEDIA_DEVICE_SYSTEM_PERMISSION_DENIED:
+    case MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED:
       web_request.RequestFailed(
           blink::WebUserMediaRequest::Error::kSystemPermissionDenied,
           "Permission denied by system");
@@ -1376,8 +1378,8 @@
           source.GetType() == blink::WebMediaStreamSource::kTypeAudio;
       NotifyCurrentRequestInfoOfAudioSourceStarted(
           source_extra_data,
-          is_audio_source ? blink::MEDIA_DEVICE_TRACK_START_FAILURE_AUDIO
-                          : blink::MEDIA_DEVICE_TRACK_START_FAILURE_VIDEO,
+          is_audio_source ? MediaStreamRequestResult::TRACK_START_FAILURE_AUDIO
+                          : MediaStreamRequestResult::TRACK_START_FAILURE_VIDEO,
           blink::WebString::FromUTF8(
               is_audio_source ? "Failed to access audio capture device"
                               : "Failed to access video capture device"));
diff --git a/content/renderer/media/stream/user_media_processor.h b/content/renderer/media/stream/user_media_processor.h
index 9ad4df2..44ba2f4 100644
--- a/content/renderer/media/stream/user_media_processor.h
+++ b/content/renderer/media/stream/user_media_processor.h
@@ -18,6 +18,7 @@
 #include "content/common/content_export.h"
 #include "content/renderer/media/stream/media_stream_dispatcher_eventhandler.h"
 #include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
 #include "third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h"
 #include "third_party/blink/public/platform/web_vector.h"
@@ -126,7 +127,7 @@
       const blink::WebMediaStream& stream,
       blink::WebUserMediaRequest web_request);
   virtual void GetUserMediaRequestFailed(
-      blink::MediaStreamRequestResult result,
+      blink::mojom::MediaStreamRequestResult result,
       const blink::WebString& constraint_name = blink::WebString());
 
   // Creates a MediaStreamAudioSource/MediaStreamVideoSource objects.
@@ -149,7 +150,7 @@
   using LocalStreamSources = std::vector<blink::WebMediaStreamSource>;
 
   void OnStreamGenerated(int request_id,
-                         blink::MediaStreamRequestResult result,
+                         blink::mojom::MediaStreamRequestResult result,
                          const std::string& label,
                          const blink::MediaStreamDevices& audio_devices,
                          const blink::MediaStreamDevices& video_devices);
@@ -163,7 +164,7 @@
   gfx::Size GetScreenSize();
 
   void OnStreamGenerationFailed(int request_id,
-                                blink::MediaStreamRequestResult result);
+                                blink::mojom::MediaStreamRequestResult result);
 
   bool IsCurrentRequestInfo(int request_id) const;
   bool IsCurrentRequestInfo(
@@ -173,7 +174,7 @@
       blink::WebUserMediaRequest web_request);
   void DelayedGetUserMediaRequestFailed(
       blink::WebUserMediaRequest web_request,
-      blink::MediaStreamRequestResult result,
+      blink::mojom::MediaStreamRequestResult result,
       const blink::WebString& constraint_name);
 
   // Called when |source| has been stopped from JavaScript.
@@ -200,10 +201,11 @@
 
   // Callback function triggered when all native versions of the
   // underlying media sources and tracks have been created and started.
-  void OnCreateNativeTracksCompleted(const std::string& label,
-                                     RequestInfo* request,
-                                     blink::MediaStreamRequestResult result,
-                                     const blink::WebString& result_name);
+  void OnCreateNativeTracksCompleted(
+      const std::string& label,
+      RequestInfo* request,
+      blink::mojom::MediaStreamRequestResult result,
+      const blink::WebString& result_name);
 
   void OnStreamGeneratedForCancelledRequest(
       const blink::MediaStreamDevices& audio_devices,
@@ -213,16 +215,16 @@
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
       base::WeakPtr<UserMediaProcessor> weak_ptr,
       blink::WebPlatformMediaStreamSource* source,
-      blink::MediaStreamRequestResult result,
+      blink::mojom::MediaStreamRequestResult result,
       const blink::WebString& result_name);
 
   void OnAudioSourceStarted(blink::WebPlatformMediaStreamSource* source,
-                            blink::MediaStreamRequestResult result,
+                            blink::mojom::MediaStreamRequestResult result,
                             const blink::WebString& result_name);
 
   void NotifyCurrentRequestInfoOfAudioSourceStarted(
       blink::WebPlatformMediaStreamSource* source,
-      blink::MediaStreamRequestResult result,
+      blink::mojom::MediaStreamRequestResult result,
       const blink::WebString& result_name);
 
   void DeleteAllUserMediaRequests();
diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source.cc b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
index b2ce120..292d5b2 100644
--- a/content/renderer/media/webrtc/media_stream_remote_video_source.cc
+++ b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
@@ -18,6 +18,7 @@
 #include "media/base/timestamp_constants.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_util.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/webrtc/api/video/i420_buffer.h"
 #include "third_party/webrtc/api/video/video_sink_interface.h"
 #include "third_party/webrtc/rtc_base/time_utils.h"  // for TimeMicros
@@ -232,7 +233,7 @@
   scoped_refptr<webrtc::VideoTrackInterface> video_track(
       static_cast<webrtc::VideoTrackInterface*>(observer_->track().get()));
   video_track->AddOrUpdateSink(delegate_.get(), rtc::VideoSinkWants());
-  OnStartDone(blink::MEDIA_DEVICE_OK);
+  OnStartDone(blink::mojom::MediaStreamRequestResult::OK);
 }
 
 void MediaStreamRemoteVideoSource::StopSourceImpl() {
diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source_unittest.cc b/content/renderer/media/webrtc/media_stream_remote_video_source_unittest.cc
index 93aff41..3b9cf19 100644
--- a/content/renderer/media/webrtc/media_stream_remote_video_source_unittest.cc
+++ b/content/renderer/media/webrtc/media_stream_remote_video_source_unittest.cc
@@ -20,6 +20,7 @@
 #include "content/renderer/media/webrtc/track_observer.h"
 #include "media/base/video_frame.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/public/web/web_heap.h"
@@ -140,10 +141,10 @@
 
  private:
   void OnTrackStarted(blink::WebPlatformMediaStreamSource* source,
-                      blink::MediaStreamRequestResult result,
+                      blink::mojom::MediaStreamRequestResult result,
                       const blink::WebString& result_name) {
     ASSERT_EQ(source, remote_source_);
-    if (result == blink::MEDIA_DEVICE_OK)
+    if (result == blink::mojom::MediaStreamRequestResult::OK)
       ++number_of_successful_track_starts_;
     else
       ++number_of_failed_track_starts_;
diff --git a/content/renderer/pepper/pepper_media_stream_video_track_host.cc b/content/renderer/pepper/pepper_media_stream_video_track_host.cc
index 12f0d7a..a3cc483 100644
--- a/content/renderer/pepper/pepper_media_stream_video_track_host.cc
+++ b/content/renderer/pepper/pepper_media_stream_video_track_host.cc
@@ -515,7 +515,7 @@
 
 void PepperMediaStreamVideoTrackHost::OnTrackStarted(
     blink::WebPlatformMediaStreamSource* source,
-    blink::MediaStreamRequestResult result,
+    blink::mojom::MediaStreamRequestResult result,
     const blink::WebString& result_name) {
   DVLOG(3) << "OnTrackStarted result: " << result;
 }
diff --git a/content/renderer/pepper/pepper_media_stream_video_track_host.h b/content/renderer/pepper/pepper_media_stream_video_track_host.h
index 1a13dfc..1b61aaa8 100644
--- a/content/renderer/pepper/pepper_media_stream_video_track_host.h
+++ b/content/renderer/pepper/pepper_media_stream_video_track_host.h
@@ -15,6 +15,7 @@
 #include "ppapi/c/ppb_video_frame.h"
 #include "ppapi/shared_impl/media_stream_video_track_shared.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_sink.h"
 #include "ui/gfx/geometry/size.h"
@@ -83,7 +84,7 @@
 
   void InitBlinkTrack();
   void OnTrackStarted(blink::WebPlatformMediaStreamSource* source,
-                      blink::MediaStreamRequestResult result,
+                      blink::mojom::MediaStreamRequestResult result,
                       const blink::WebString& result_name);
 
   blink::WebMediaStreamTrack track_;
diff --git a/content/renderer/worker/dedicated_worker_host_factory_client.cc b/content/renderer/worker/dedicated_worker_host_factory_client.cc
index 308587b6..34c57115 100644
--- a/content/renderer/worker/dedicated_worker_host_factory_client.cc
+++ b/content/renderer/worker/dedicated_worker_host_factory_client.cc
@@ -15,6 +15,8 @@
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
 #include "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom.h"
 #include "third_party/blink/public/platform/web_dedicated_worker.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/public/platform/web_url.h"
 
 namespace content {
 
@@ -52,6 +54,29 @@
       std::move(client_ptr));
 }
 
+scoped_refptr<blink::WebWorkerFetchContext>
+DedicatedWorkerHostFactoryClient::CloneWorkerFetchContext(
+    blink::WebWorkerFetchContext* web_worker_fetch_context,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  scoped_refptr<WebWorkerFetchContextImpl> worker_fetch_context;
+  if (blink::features::IsPlzDedicatedWorkerEnabled()) {
+    worker_fetch_context =
+        static_cast<WebWorkerFetchContextImpl*>(web_worker_fetch_context)
+            ->CloneForNestedWorker(service_worker_provider_context_.get(),
+                                   subresource_loader_factory_bundle_->Clone(),
+                                   subresource_loader_factory_bundle_
+                                       ->CloneWithoutAppCacheFactory(),
+                                   std::move(task_runner));
+    worker_fetch_context->SetResponseOverrideForMainScript(
+        std::move(response_override_for_main_script_));
+  } else {
+    worker_fetch_context =
+        static_cast<WebWorkerFetchContextImpl*>(web_worker_fetch_context)
+            ->CloneForNestedWorkerDeprecated(std::move(task_runner));
+  }
+  return worker_fetch_context;
+}
+
 scoped_refptr<WebWorkerFetchContextImpl>
 DedicatedWorkerHostFactoryClient::CreateWorkerFetchContext(
     blink::mojom::RendererPreferences renderer_preference,
diff --git a/content/renderer/worker/dedicated_worker_host_factory_client.h b/content/renderer/worker/dedicated_worker_host_factory_client.h
index 539e07c5..b41135f7 100644
--- a/content/renderer/worker/dedicated_worker_host_factory_client.h
+++ b/content/renderer/worker/dedicated_worker_host_factory_client.h
@@ -15,6 +15,7 @@
 
 namespace blink {
 class WebDedicatedWorker;
+class WebWorkerFetchContext;
 }  // namespace blink
 
 namespace content {
@@ -44,6 +45,9 @@
   void CreateWorkerHost(const blink::WebURL& script_url,
                         const blink::WebSecurityOrigin& script_origin,
                         mojo::ScopedMessagePipeHandle blob_url_token) override;
+  scoped_refptr<blink::WebWorkerFetchContext> CloneWorkerFetchContext(
+      blink::WebWorkerFetchContext* web_worker_fetch_context,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
 
   scoped_refptr<WebWorkerFetchContextImpl> CreateWorkerFetchContext(
       blink::mojom::RendererPreferences renderer_preference,
diff --git a/content/renderer/worker/worker_thread_registry_unittest.cc b/content/renderer/worker/worker_thread_registry_unittest.cc
index 299261fd8..3237e7be 100644
--- a/content/renderer/worker/worker_thread_registry_unittest.cc
+++ b/content/renderer/worker/worker_thread_registry_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "base/test/scoped_task_environment.h"
+#include "build/build_config.h"
 #include "content/public/renderer/worker_thread.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -33,7 +34,13 @@
   WorkerThreadRegistry* runner_;
 };
 
-TEST_F(WorkerThreadRegistryTest, BasicObservingAndWorkerId) {
+// TODO(https://crbug.com/969562): Fix flaky failure on Fuschia.
+#if defined(OS_FUCHSIA)
+#define MAYBE_BasicObservingAndWorkerId DISABLED_BasicObservingAndWorkerId
+#else
+#define MAYBE_BasicObservingAndWorkerId BasicObservingAndWorkerId
+#endif
+TEST_F(WorkerThreadRegistryTest, MAYBE_BasicObservingAndWorkerId) {
   ASSERT_EQ(0, WorkerThread::GetCurrentId());
   MockObserver o;
   EXPECT_CALL(o, WillStopCurrentWorkerThread()).Times(1);
@@ -53,7 +60,13 @@
   FakeStop();
 }
 
-TEST_F(WorkerThreadRegistryTest, TaskRunnerRemovedCorrectly) {
+// TODO(https://crbug.com/969562): Fix flaky failure on Fuschia.
+#if defined(OS_FUCHSIA)
+#define MAYBE_TaskRunnerRemovedCorrectly DISABLED_TaskRunnerRemovedCorrectly
+#else
+#define MAYBE_TaskRunnerRemovedCorrectly TaskRunnerRemovedCorrectly
+#endif
+TEST_F(WorkerThreadRegistryTest, MAYBE_TaskRunnerRemovedCorrectly) {
   ASSERT_EQ(0, WorkerThread::GetCurrentId());
   MockObserver o;
   FakeStart();
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index a0afb5c..c03e6ab2 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1716,8 +1716,6 @@
     "../browser/startup_task_runner_unittest.cc",
     "../browser/storage_partition_impl_map_unittest.cc",
     "../browser/storage_partition_impl_unittest.cc",
-    "../browser/streams/stream_unittest.cc",
-    "../browser/streams/stream_url_request_job_unittest.cc",
     "../browser/tracing/background_startup_tracing_observer_unittest.cc",
     "../browser/tracing/background_tracing_config_unittest.cc",
     "../browser/tracing/tracing_ui_unittest.cc",
diff --git a/content/test/test_navigation_url_loader.cc b/content/test/test_navigation_url_loader.cc
index 7a26247b..555f76a 100644
--- a/content/test/test_navigation_url_loader.cc
+++ b/content/test/test_navigation_url_loader.cc
@@ -95,8 +95,7 @@
 
   delegate_->OnResponseStarted(response, std::move(url_loader_client_endpoints),
                                std::move(navigation_data), global_id, false,
-                               NavigationDownloadPolicy(), false,
-                               base::nullopt);
+                               NavigationDownloadPolicy(), base::nullopt);
 }
 
 TestNavigationURLLoader::~TestNavigationURLLoader() {}
diff --git a/content/test/test_navigation_url_loader_delegate.cc b/content/test/test_navigation_url_loader_delegate.cc
index 647d412..05a7a5d3 100644
--- a/content/test/test_navigation_url_loader_delegate.cc
+++ b/content/test/test_navigation_url_loader_delegate.cc
@@ -63,7 +63,6 @@
     const GlobalRequestID& request_id,
     bool is_download,
     NavigationDownloadPolicy download_policy,
-    bool is_stream,
     base::Optional<SubresourceLoaderParams> subresource_loader_params) {
   response_ = response;
   url_loader_client_endpoints_ = std::move(url_loader_client_endpoints);
diff --git a/content/test/test_navigation_url_loader_delegate.h b/content/test/test_navigation_url_loader_delegate.h
index 0e575517..5abfe62b 100644
--- a/content/test/test_navigation_url_loader_delegate.h
+++ b/content/test/test_navigation_url_loader_delegate.h
@@ -67,7 +67,6 @@
       const GlobalRequestID& request_id,
       bool is_download,
       NavigationDownloadPolicy download_policy,
-      bool is_stream,
       base::Optional<SubresourceLoaderParams> subresource_loader_params)
       override;
   void OnRequestFailed(
diff --git a/docs/media/gpu/video_decoder_perf_test_usage.md b/docs/media/gpu/video_decoder_perf_test_usage.md
index 029288f5..be6b64e 100644
--- a/docs/media/gpu/video_decoder_perf_test_usage.md
+++ b/docs/media/gpu/video_decoder_perf_test_usage.md
@@ -56,6 +56,9 @@
      -v                  enable verbose mode, e.g. -v=2.
     --vmodule            enable verbose mode for the specified module,
                          e.g. --vmodule=*media/gpu*=2.
+    --output_folder      overwrite the output folder used to store
+                         performance metrics, if not specified results
+                         will be stored in the current working directory.
     --use_vd             use the new VD-based video decoders, instead of
                          the default VDA-based video decoders.
     --gtest_help         display the gtest help and exit.
diff --git a/docs/media/gpu/video_decoder_test_usage.md b/docs/media/gpu/video_decoder_test_usage.md
index cc1542f..6d091737 100644
--- a/docs/media/gpu/video_decoder_test_usage.md
+++ b/docs/media/gpu/video_decoder_test_usage.md
@@ -67,6 +67,8 @@
                          platforms that don't support import mode.
     --output_frames      write all decoded video frames to the
                          "video_frames" folder.
+    --output_folder      overwrite the default output folder used when
+                         "--output_frames" is specified.
     --use_vd             use the new VD-based video decoders, instead of
                          the default VDA-based video decoders.
     --gtest_help         display the gtest help and exit.
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index bf35694..0aa6274e 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -588,7 +588,6 @@
     "api/file_handlers/directory_util_unittest.cc",
     "api/file_handlers/mime_util_unittest.cc",
     "api/idle/idle_api_unittest.cc",
-    "api/mime_handler_private/mime_handler_private_unittest.cc",
     "api/networking_config/networking_config_service_chromeos_unittest.cc",
     "api/networking_private/networking_private_chromeos_unittest.cc",
     "api/power/power_api_unittest.cc",
diff --git a/extensions/browser/api/mime_handler_private/mime_handler_private.cc b/extensions/browser/api/mime_handler_private/mime_handler_private.cc
index f55b49e2..8ac9ec8 100644
--- a/extensions/browser/api/mime_handler_private/mime_handler_private.cc
+++ b/extensions/browser/api/mime_handler_private/mime_handler_private.cc
@@ -9,8 +9,6 @@
 #include "base/bind.h"
 #include "base/containers/flat_map.h"
 #include "base/strings/string_util.h"
-#include "content/public/browser/stream_handle.h"
-#include "content/public/browser/stream_info.h"
 #include "content/public/common/content_constants.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
 #include "extensions/common/constants.h"
diff --git a/extensions/browser/api/mime_handler_private/mime_handler_private_unittest.cc b/extensions/browser/api/mime_handler_private/mime_handler_private_unittest.cc
deleted file mode 100644
index 1ba5190..0000000
--- a/extensions/browser/api/mime_handler_private/mime_handler_private_unittest.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "extensions/browser/api/mime_handler_private/mime_handler_private.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/containers/queue.h"
-#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
-#include "content/public/browser/stream_handle.h"
-#include "content/public/browser/stream_info.h"
-#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace extensions {
-
-class TestStreamHandle : public content::StreamHandle {
- public:
-  TestStreamHandle() : url_("stream://url") {}
-
-  ~TestStreamHandle() override {
-    while (!close_callbacks_.empty()) {
-      close_callbacks_.front().Run();
-      close_callbacks_.pop();
-    }
-  }
-
-  const GURL& GetURL() override { return url_; }
-
-  void AddCloseListener(const base::Closure& callback) override {
-    close_callbacks_.push(callback);
-  }
-
- private:
-  GURL url_;
-  base::queue<base::Closure> close_callbacks_;
-};
-
-class MimeHandlerServiceImplTest : public testing::Test {
- public:
-  void SetUp() override {
-    std::unique_ptr<content::StreamInfo> stream_info(new content::StreamInfo);
-    stream_info->handle = base::WrapUnique(new TestStreamHandle);
-    stream_info->mime_type = "test/unit";
-    stream_info->original_url = GURL("test://extensions_unittests");
-    stream_container_ = std::make_unique<StreamContainer>(
-        std::move(stream_info), 1, true, GURL(), "", nullptr, GURL());
-    service_binding_ =
-        mojo::MakeStrongBinding(std::make_unique<MimeHandlerServiceImpl>(
-                                    stream_container_->GetWeakPtr()),
-                                mojo::MakeRequest(&service_ptr_));
-  }
-  void TearDown() override {
-    service_binding_->Close();
-    stream_container_.reset();
-  }
-
-  void AbortCallback() { abort_called_ = true; }
-  void GetStreamInfoCallback(mime_handler::StreamInfoPtr stream_info) {
-    stream_info_ = std::move(stream_info);
-  }
-
-  base::MessageLoop message_loop_;
-  std::unique_ptr<StreamContainer> stream_container_;
-  mime_handler::MimeHandlerServicePtr service_ptr_;
-  mojo::StrongBindingPtr<mime_handler::MimeHandlerService> service_binding_;
-  bool abort_called_ = false;
-  mime_handler::StreamInfoPtr stream_info_;
-};
-
-TEST_F(MimeHandlerServiceImplTest, Abort) {
-  service_binding_->impl()->AbortStream(base::Bind(
-      &MimeHandlerServiceImplTest::AbortCallback, base::Unretained(this)));
-  EXPECT_TRUE(abort_called_);
-
-  abort_called_ = false;
-  service_binding_->impl()->AbortStream(base::Bind(
-      &MimeHandlerServiceImplTest::AbortCallback, base::Unretained(this)));
-  EXPECT_TRUE(abort_called_);
-
-  stream_container_.reset();
-  abort_called_ = false;
-  service_binding_->impl()->AbortStream(base::Bind(
-      &MimeHandlerServiceImplTest::AbortCallback, base::Unretained(this)));
-  EXPECT_TRUE(abort_called_);
-}
-
-TEST_F(MimeHandlerServiceImplTest, GetStreamInfo) {
-  service_binding_->impl()->GetStreamInfo(
-      base::Bind(&MimeHandlerServiceImplTest::GetStreamInfoCallback,
-                 base::Unretained(this)));
-  ASSERT_TRUE(stream_info_);
-  EXPECT_TRUE(stream_info_->embedded);
-  EXPECT_EQ(1, stream_info_->tab_id);
-  EXPECT_EQ("test/unit", stream_info_->mime_type);
-  EXPECT_EQ("test://extensions_unittests", stream_info_->original_url);
-  EXPECT_EQ("stream://url", stream_info_->stream_url);
-
-  service_binding_->impl()->AbortStream(base::Bind(
-      &MimeHandlerServiceImplTest::AbortCallback, base::Unretained(this)));
-  EXPECT_TRUE(abort_called_);
-  service_binding_->impl()->GetStreamInfo(
-      base::Bind(&MimeHandlerServiceImplTest::GetStreamInfoCallback,
-                 base::Unretained(this)));
-  ASSERT_FALSE(stream_info_);
-
-  stream_container_.reset();
-  service_binding_->impl()->GetStreamInfo(
-      base::Bind(&MimeHandlerServiceImplTest::GetStreamInfoCallback,
-                 base::Unretained(this)));
-  ASSERT_FALSE(stream_info_);
-}
-
-}  // namespace extensions
diff --git a/extensions/browser/guest_view/extensions_guest_view_message_filter.cc b/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
index a7e9ed1..55c88ac 100644
--- a/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
+++ b/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
@@ -16,7 +16,6 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/browser/stream_info.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/mime_handler_view_mode.h"
 #include "extensions/browser/api/extensions_api_client.h"
@@ -239,7 +238,7 @@
 
   std::string view_id = base::GenerateGUID();
   std::unique_ptr<StreamContainer> stream_container(new StreamContainer(
-      nullptr, tab_id, true /* embedded */, handler_url, extension_id,
+      tab_id, true /* embedded */, handler_url, extension_id,
       std::move(transferrable_url_loader), original_url));
   MimeHandlerStreamManager::Get(browser_context)
       ->AddStream(view_id, std::move(stream_container),
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
index cf367d3..0bb8c44 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
@@ -14,8 +14,6 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_view.h"
-#include "content/public/browser/stream_handle.h"
-#include "content/public/browser/stream_info.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/mime_handler_view_mode.h"
 #include "content/public/common/url_constants.h"
@@ -45,41 +43,28 @@
 namespace extensions {
 
 StreamContainer::StreamContainer(
-    std::unique_ptr<content::StreamInfo> stream,
     int tab_id,
     bool embedded,
     const GURL& handler_url,
     const std::string& extension_id,
     content::mojom::TransferrableURLLoaderPtr transferrable_loader,
     const GURL& original_url)
-    : stream_(std::move(stream)),
-      embedded_(embedded),
+    : embedded_(embedded),
       tab_id_(tab_id),
       handler_url_(handler_url),
       extension_id_(extension_id),
       transferrable_loader_(std::move(transferrable_loader)),
-      mime_type_(stream_ ? stream_->mime_type
-                         : transferrable_loader_->head.mime_type),
-      original_url_(stream_ ? stream_->original_url : original_url),
-      stream_url_(stream_ ? stream_->handle->GetURL()
-                          : transferrable_loader_->url),
-      response_headers_(stream_ ? stream_->response_headers
-                                : transferrable_loader_->head.headers),
-      weak_factory_(this) {
-  DCHECK(stream_ || transferrable_loader_);
-}
+      mime_type_(transferrable_loader_->head.mime_type),
+      original_url_(original_url),
+      stream_url_(transferrable_loader_->url),
+      response_headers_(transferrable_loader_->head.headers),
+      weak_factory_(this) {}
 
 StreamContainer::~StreamContainer() {
 }
 
 void StreamContainer::Abort(const base::Closure& callback) {
-  if (!stream_ || !stream_->handle) {
-    callback.Run();
-    return;
-  }
-  stream_->handle->AddCloseListener(callback);
-  stream_->handle.reset();
-  stream_url_ = GURL();
+  callback.Run();
 }
 
 base::WeakPtr<StreamContainer> StreamContainer::GetWeakPtr() {
@@ -280,8 +265,9 @@
 WebContents* MimeHandlerViewGuest::OpenURLFromTab(
     WebContents* source,
     const content::OpenURLParams& params) {
-  return embedder_web_contents()->GetDelegate()->OpenURLFromTab(
-      embedder_web_contents(), params);
+  auto* delegate = embedder_web_contents()->GetDelegate();
+  return delegate ? delegate->OpenURLFromTab(embedder_web_contents(), params)
+                  : nullptr;
 }
 
 void MimeHandlerViewGuest::NavigationStateChanged(
@@ -303,8 +289,9 @@
   if (last_committed_entry) {
     embedder_web_contents()->UpdateTitleForEntry(last_committed_entry,
                                                  source->GetTitle());
-    embedder_web_contents()->GetDelegate()->NavigationStateChanged(
-        embedder_web_contents(), changed_flags);
+    auto* delegate = embedder_web_contents()->GetDelegate();
+    if (delegate)
+      delegate->NavigationStateChanged(embedder_web_contents(), changed_flags);
   }
 }
 
@@ -337,8 +324,9 @@
   // So we pretend to be our owner WebContents, but only for the request to
   // obtain the JavaScriptDialogManager. During calls to the
   // JavaScriptDialogManager we will be honest about who we are.
-  return owner_web_contents()->GetDelegate()->GetJavaScriptDialogManager(
-      owner_web_contents());
+  auto* delegate = owner_web_contents()->GetDelegate();
+  return delegate ? delegate->GetJavaScriptDialogManager(owner_web_contents())
+                  : nullptr;
 }
 
 bool MimeHandlerViewGuest::PluginDoSave() {
@@ -386,15 +374,19 @@
     const GURL& origin,
     const blink::WebFullscreenOptions& options) {
   if (SetFullscreenState(true)) {
-    embedder_web_contents()->GetDelegate()->EnterFullscreenModeForTab(
-        embedder_web_contents(), origin, options);
+    auto* delegate = embedder_web_contents()->GetDelegate();
+    if (delegate) {
+      delegate->EnterFullscreenModeForTab(embedder_web_contents(), origin,
+                                          options);
+    }
   }
 }
 
 void MimeHandlerViewGuest::ExitFullscreenModeForTab(content::WebContents*) {
   if (SetFullscreenState(false)) {
-    embedder_web_contents()->GetDelegate()->ExitFullscreenModeForTab(
-        embedder_web_contents());
+    auto* delegate = embedder_web_contents()->GetDelegate();
+    if (delegate)
+      delegate->ExitFullscreenModeForTab(embedder_web_contents());
   }
 }
 
@@ -423,8 +415,9 @@
   // Extensions are allowed to open popups under circumstances covered by
   // running as a mime handler.
   open_params.user_gesture = true;
-  embedder_web_contents()->GetDelegate()->OpenURLFromTab(
-      embedder_web_contents(), open_params);
+  auto* delegate = embedder_web_contents()->GetDelegate();
+  if (delegate)
+    delegate->OpenURLFromTab(embedder_web_contents(), open_params);
   return false;
 }
 
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h
index 7cdbfafb..0a4855e7 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h
@@ -18,18 +18,16 @@
 namespace content {
 class WebContents;
 struct ContextMenuParams;
-struct StreamInfo;
 }  // namespace content
 
 namespace extensions {
 class MimeHandlerViewGuestDelegate;
 
-// A container for a StreamHandle and any other information necessary for a
-// MimeHandler to handle a resource stream.
+// A container for a information necessary for a MimeHandler to handle a
+// resource stream.
 class StreamContainer {
  public:
   StreamContainer(
-      std::unique_ptr<content::StreamInfo> stream,
       int tab_id,
       bool embedded,
       const GURL& handler_url,
@@ -58,7 +56,6 @@
   }
 
  private:
-  const std::unique_ptr<content::StreamInfo> stream_;
   const bool embedded_;
   const int tab_id_;
   const GURL handler_url_;
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
index dad88f7c..663a36f 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
@@ -20,6 +20,7 @@
 #include "extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h"
 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
 #include "ppapi/buildflags/buildflags.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 using base::UserMetricsAction;
 using content::BrowserPluginGuestDelegate;
@@ -224,16 +225,18 @@
     bool allow,
     const std::string& user_input) {
   if (!allow) {
-    std::move(callback).Run(blink::MediaStreamDevices(),
-                            blink::MEDIA_DEVICE_PERMISSION_DENIED,
-                            std::unique_ptr<content::MediaStreamUI>());
+    std::move(callback).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+        std::unique_ptr<content::MediaStreamUI>());
     return;
   }
   if (!web_view_guest()->attached() ||
       !web_view_guest()->embedder_web_contents()->GetDelegate()) {
-    std::move(callback).Run(blink::MediaStreamDevices(),
-                            blink::MEDIA_DEVICE_INVALID_STATE,
-                            std::unique_ptr<content::MediaStreamUI>());
+    std::move(callback).Run(
+        blink::MediaStreamDevices(),
+        blink::mojom::MediaStreamRequestResult::INVALID_STATE,
+        std::unique_ptr<content::MediaStreamUI>());
     return;
   }
 
diff --git a/extensions/browser/media_capture_util.cc b/extensions/browser/media_capture_util.cc
index ef0fa85..4b3ad0d 100644
--- a/extensions/browser/media_capture_util.cc
+++ b/extensions/browser/media_capture_util.cc
@@ -13,6 +13,7 @@
 #include "content/public/browser/media_capture_devices.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/permissions/permissions_data.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 using blink::MediaStreamDevice;
 using blink::MediaStreamDevices;
@@ -76,10 +77,11 @@
 
   // TODO(jamescook): Should we show a recording icon somewhere? If so, where?
   std::unique_ptr<MediaStreamUI> ui;
-  std::move(callback).Run(devices,
-                          devices.empty() ? blink::MEDIA_DEVICE_INVALID_STATE
-                                          : blink::MEDIA_DEVICE_OK,
-                          std::move(ui));
+  std::move(callback).Run(
+      devices,
+      devices.empty() ? blink::mojom::MediaStreamRequestResult::INVALID_STATE
+                      : blink::mojom::MediaStreamRequestResult::OK,
+      std::move(ui));
 }
 
 void VerifyMediaAccessPermission(blink::MediaStreamType type,
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index 5646671..1a76fc8 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -2728,7 +2728,7 @@
     }
     builders {
       name: "ios-device-goma-canary-clobber"
-      dimensions: "os:Mac-10.13"
+      dimensions: "os:Mac"
       dimensions: "cores:4"
       mixins: "fyi-ci"
       recipe {
@@ -3201,7 +3201,7 @@
     }
     builders {
       name: "ios-device-goma-latest-clobber"
-      dimensions: "os:Mac-10.13"
+      dimensions: "os:Mac"
       dimensions: "cores:4"
       mixins: "fyi-ci"
       recipe {
diff --git a/ios/chrome/browser/signin/identity_manager_factory.cc b/ios/chrome/browser/signin/identity_manager_factory.cc
index de376f5..0d7e6e2 100644
--- a/ios/chrome/browser/signin/identity_manager_factory.cc
+++ b/ios/chrome/browser/signin/identity_manager_factory.cc
@@ -145,7 +145,8 @@
 
   auto primary_account_mutator =
       std::make_unique<identity::PrimaryAccountMutatorImpl>(
-          account_tracker_service.get(), signin_manager.get());
+          account_tracker_service.get(), signin_manager.get(),
+          browser_state->GetPrefs());
 
   auto accounts_cookie_mutator =
       std::make_unique<identity::AccountsCookieMutatorImpl>(
diff --git a/ios/testing/earl_grey/BUILD.gn b/ios/testing/earl_grey/BUILD.gn
index 22cf840..7a5367b 100644
--- a/ios/testing/earl_grey/BUILD.gn
+++ b/ios/testing/earl_grey/BUILD.gn
@@ -15,8 +15,6 @@
   ]
 
   sources = [
-    "app_launch_manager.h",
-    "app_launch_manager.mm",
     "base_earl_grey_test_case.h",
     "base_earl_grey_test_case.mm",
     "base_eg_test_helper_impl.h",
@@ -61,8 +59,6 @@
   testonly = true
 
   sources = [
-    "app_launch_manager.h",
-    "app_launch_manager.mm",
     "base_earl_grey_test_case.h",
     "base_earl_grey_test_case.mm",
     "base_eg_test_helper_impl.h",
diff --git a/ios/testing/earl_grey/app_launch_manager.h b/ios/testing/earl_grey/app_launch_manager.h
deleted file mode 100644
index b03205e8..0000000
--- a/ios/testing/earl_grey/app_launch_manager.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_TESTING_EARL_GREY_APP_LAUNCH_MANAGER_H_
-#define IOS_TESTING_EARL_GREY_APP_LAUNCH_MANAGER_H_
-
-#import <Foundation/Foundation.h>
-
-// Provides control of the single application-under-test to EarlGrey 2 tests.
-@interface AppLaunchManager : NSObject
-
-// Returns the singleton instance of this class.
-+ (AppLaunchManager*)sharedManager;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-// Makes sure the app has been started with the appropriate |arguments|.
-// In EG2, will launch the app if any of the following conditions are met:
-// * The app is not running
-// * The app is currently running with different arguments.
-// * |forceRestart| is YES
-// Otherwise, the app will be activated instead of (re)launched.
-// Will wait until app is activated or launched, and fail the test if it
-// fails to do so.
-// In EG1, this method is a no-op.
-- (void)ensureAppLaunchedWithArgs:(NSArray<NSString*>*)arguments
-                     forceRestart:(BOOL)forceRestart;
-
-@end
-
-#endif  // IOS_TESTING_EARL_GREY_APP_LAUNCH_MANAGER_H_
diff --git a/ios/testing/earl_grey/app_launch_manager.mm b/ios/testing/earl_grey/app_launch_manager.mm
deleted file mode 100644
index 7164036c..0000000
--- a/ios/testing/earl_grey/app_launch_manager.mm
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/testing/earl_grey/app_launch_manager.h"
-
-#import <XCTest/XCTest.h>
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-#if defined(CHROME_EARL_GREY_2)  // avoid unused function warning in EG1
-namespace {
-// Checks if two pairs of launch arguments are equivalent.
-bool LaunchArgumentsAreEqual(NSArray<NSString*>* args1,
-                             NSArray<NSString*>* args2) {
-  // isEqualToArray will only return true if both arrays are non-nil,
-  // so first check if both arrays are empty or nil
-  if (!args1.count && !args2.count) {
-    return true;
-  }
-
-  return [args1 isEqualToArray:args2];
-}
-}  // namespace
-#endif
-
-@interface AppLaunchManager ()
-@property(nonatomic) XCUIApplication* runningApplication;
-@property(nonatomic) NSArray<NSString*>* currentLaunchArgs;
-@end
-
-@implementation AppLaunchManager
-
-+ (AppLaunchManager*)sharedManager {
-  static AppLaunchManager* instance = nil;
-  static dispatch_once_t guard;
-  dispatch_once(&guard, ^{
-    instance = [[AppLaunchManager alloc] init];
-  });
-  return instance;
-}
-
-- (void)ensureAppLaunchedWithArgs:(NSArray<NSString*>*)arguments
-                     forceRestart:(BOOL)forceRestart {
-#if defined(CHROME_EARL_GREY_2)
-  bool appNeedsLaunching =
-      forceRestart || !self.runningApplication ||
-      !LaunchArgumentsAreEqual(arguments, self.currentLaunchArgs);
-
-  if (!appNeedsLaunching) {
-    [self.runningApplication activate];
-    return;
-  }
-
-  XCUIApplication* application = [[XCUIApplication alloc] init];
-  application.launchArguments = arguments;
-
-  [application launch];
-  self.runningApplication = application;
-  self.currentLaunchArgs = arguments;
-#endif
-}
-@end
diff --git a/ios/testing/earl_grey/base_earl_grey_test_case.mm b/ios/testing/earl_grey/base_earl_grey_test_case.mm
index cb4f8a3..fe496a9 100644
--- a/ios/testing/earl_grey/base_earl_grey_test_case.mm
+++ b/ios/testing/earl_grey/base_earl_grey_test_case.mm
@@ -7,7 +7,6 @@
 #import <UIKit/UIKit.h>
 #import <objc/runtime.h>
 
-#import "ios/testing/earl_grey/app_launch_manager.h"
 #import "ios/testing/earl_grey/coverage_utils.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
 
@@ -42,8 +41,11 @@
 }
 
 - (void)launchAppForTestMethod {
-  [[AppLaunchManager sharedManager] ensureAppLaunchedWithArgs:nil
-                                                 forceRestart:false];
+  static dispatch_once_t launchAppToken;
+  dispatch_once(&launchAppToken, ^{
+    XCUIApplication* application = [[XCUIApplication alloc] init];
+    [application launch];
+  });
 }
 
 // Prevents tests inheriting from this class from putting logic in +setUp.
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index a18d25ab..0c467c8 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -497,6 +497,7 @@
     "//ios/net",
     "//ios/testing:ocmock_support",
     "//ios/web/find_in_page",
+    "//ios/web/js_messaging",
     "//ios/web/public",
     "//ios/web/public/deprecated",
     "//ios/web/public/js_messaging",
@@ -507,7 +508,6 @@
     "//ios/web/test:test_support",
     "//ios/web/web_state:context_menu",
     "//ios/web/web_state/js",
-    "//ios/web/web_state/js:script_util",
     "//ios/web/web_state/ui:crw_wk_script_message_router",
     "//net:test_support",
     "//services/service_manager/public/cpp",
@@ -521,10 +521,8 @@
     "web_state/js/common_js_unittest.mm",
     "web_state/js/context_menu_js_unittest.mm",
     "web_state/js/crw_js_injection_manager_unittest.mm",
-    "web_state/js/crw_js_window_id_manager_unittest.mm",
     "web_state/js/find_in_page_js_unittest.mm",
     "web_state/js/message_js_unittest.mm",
-    "web_state/js/page_script_util_unittest.mm",
   ]
 }
 
@@ -540,6 +538,7 @@
     "//ios/net",
     "//ios/testing:ocmock_support",
     "//ios/web/common",
+    "//ios/web/js_messaging",
     "//ios/web/navigation",
     "//ios/web/navigation:block_universal_links_buildflags",
     "//ios/web/navigation:core",
@@ -555,11 +554,9 @@
     "//ios/web/test/fakes:fakes",
     "//ios/web/web_state:context_menu",
     "//ios/web/web_state/js",
-    "//ios/web/web_state/js:script_util",
     "//ios/web/web_state/ui:crw_context_menu_controller",
     "//ios/web/web_state/ui:crw_wk_script_message_router",
     "//ios/web/web_state/ui:favicon_util",
-    "//ios/web/web_state/ui:web_view_js_utils",
     "//net:test_support",
     "//services/service_manager/public/cpp",
     "//testing/gmock",
@@ -693,12 +690,12 @@
   closure_entry_point = "__crWeb.allFramesWebBundle"
 
   sources = [
+    "js_messaging/resources/message.js",
     "web_state/js/resources/all_frames_context_menu.js",
     "web_state/js/resources/all_frames_web_bundle.js",
     "web_state/js/resources/base.js",
     "web_state/js/resources/common.js",
     "web_state/js/resources/find_in_page.js",
-    "web_state/js/resources/message.js",
   ]
 }
 
@@ -707,9 +704,9 @@
   closure_entry_point = "__crWeb.allFramesDocumentEndWebBundle"
 
   sources = [
+    "js_messaging/resources/setup_frame.js",
     "web_state/js/resources/all_frames_document_end_web_bundle.js",
     "web_state/js/resources/plugin_placeholder.js",
-    "web_state/js/resources/setup_frame.js",
   ]
 }
 
@@ -731,7 +728,7 @@
   ]
 
   sources = [
-    "web_state/js/resources/window_id.js",
+    "js_messaging/resources/window_id.js",
   ]
 }
 
diff --git a/ios/web/js_compile.gni b/ios/web/js_compile.gni
index 83bcc0b..c7051bb 100644
--- a/ios/web/js_compile.gni
+++ b/ios/web/js_compile.gni
@@ -122,7 +122,7 @@
       _js_modules = [
         "//ios/web/web_state/js/resources/base.js",
         "//ios/web/web_state/js/resources/common.js",
-        "//ios/web/web_state/js/resources/message.js",
+        "//ios/web/js_messaging/resources/message.js",
       ]
       if (defined(invoker.js_modules)) {
         _js_modules += invoker.js_modules
diff --git a/ios/web/js_messaging/BUILD.gn b/ios/web/js_messaging/BUILD.gn
index e65f92fc7..7339ed9 100644
--- a/ios/web/js_messaging/BUILD.gn
+++ b/ios/web/js_messaging/BUILD.gn
@@ -10,17 +10,26 @@
     "//base",
     "//crypto",
     "//ios/web/public",
+    "//ios/web/public/deprecated",
     "//ios/web/public/js_messaging",
     "//ios/web/web_state:web_state_impl_header",
     "//url",
   ]
 
   sources = [
+    "crw_js_injector.h",
+    "crw_js_injector.mm",
+    "crw_js_window_id_manager.h",
+    "crw_js_window_id_manager.mm",
+    "page_script_util.h",
+    "page_script_util.mm",
     "web_frame_impl.h",
     "web_frame_impl.mm",
     "web_frame_util.mm",
     "web_frames_manager_impl.h",
     "web_frames_manager_impl.mm",
+    "web_view_js_utils.h",
+    "web_view_js_utils.mm",
   ]
 }
 
@@ -39,6 +48,8 @@
   ]
 
   sources = [
+    "crw_js_window_id_manager_unittest.mm",
+    "page_script_util_unittest.mm",
     "web_frame_impl_unittest.mm",
     "web_frame_util_unittest.mm",
     "web_frames_manager_impl_unittest.mm",
diff --git a/ios/web/web_state/ui/crw_js_injector.h b/ios/web/js_messaging/crw_js_injector.h
similarity index 91%
rename from ios/web/web_state/ui/crw_js_injector.h
rename to ios/web/js_messaging/crw_js_injector.h
index f6bb527..3642ac5a 100644
--- a/ios/web/web_state/ui/crw_js_injector.h
+++ b/ios/web/js_messaging/crw_js_injector.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_WEB_STATE_UI_CRW_JS_INJECTOR_H_
-#define IOS_WEB_WEB_STATE_UI_CRW_JS_INJECTOR_H_
+#ifndef IOS_WEB_JS_MESSAGING_CRW_JS_INJECTOR_H_
+#define IOS_WEB_JS_MESSAGING_CRW_JS_INJECTOR_H_
 
 #import <UIKit/UIKit.h>
 
@@ -49,4 +49,4 @@
 
 @end
 
-#endif  // IOS_WEB_WEB_STATE_UI_CRW_JS_INJECTOR_H_
+#endif  // IOS_WEB_JS_MESSAGING_CRW_JS_INJECTOR_H_
diff --git a/ios/web/web_state/ui/crw_js_injector.mm b/ios/web/js_messaging/crw_js_injector.mm
similarity index 95%
rename from ios/web/web_state/ui/crw_js_injector.mm
rename to ios/web/js_messaging/crw_js_injector.mm
index 9adc4b3..2a15dc5 100644
--- a/ios/web/web_state/ui/crw_js_injector.mm
+++ b/ios/web/js_messaging/crw_js_injector.mm
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/ui/crw_js_injector.h"
+#import "ios/web/js_messaging/crw_js_injector.h"
 
 #import <WebKit/WebKit.h>
 
 #include "base/logging.h"
+#import "ios/web/js_messaging/crw_js_window_id_manager.h"
+#import "ios/web/js_messaging/web_view_js_utils.h"
 #import "ios/web/public/deprecated/crw_js_injection_manager.h"
 #import "ios/web/public/deprecated/crw_js_injection_receiver.h"
 #import "ios/web/public/web_client.h"
-#import "ios/web/web_state/js/crw_js_window_id_manager.h"
-#import "ios/web/web_state/ui/web_view_js_utils.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/web/web_state/js/crw_js_window_id_manager.h b/ios/web/js_messaging/crw_js_window_id_manager.h
similarity index 82%
rename from ios/web/web_state/js/crw_js_window_id_manager.h
rename to ios/web/js_messaging/crw_js_window_id_manager.h
index f334b6a..dbb5177f 100644
--- a/ios/web/web_state/js/crw_js_window_id_manager.h
+++ b/ios/web/js_messaging/crw_js_window_id_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_WEB_STATE_JS_CRW_JS_WINDOW_ID_MANAGER_H_
-#define IOS_WEB_WEB_STATE_JS_CRW_JS_WINDOW_ID_MANAGER_H_
+#ifndef IOS_WEB_JS_MESSAGING_CRW_JS_WINDOW_ID_MANAGER_H_
+#define IOS_WEB_JS_MESSAGING_CRW_JS_WINDOW_ID_MANAGER_H_
 
 #import <Foundation/Foundation.h>
 #import <WebKit/WebKit.h>
@@ -26,4 +26,4 @@
 
 @end
 
-#endif  // IOS_WEB_WEB_STATE_JS_CRW_JS_WINDOW_ID_MANAGER_H_
+#endif  // IOS_WEB_JS_MESSAGING_CRW_JS_WINDOW_ID_MANAGER_H_
diff --git a/ios/web/web_state/js/crw_js_window_id_manager.mm b/ios/web/js_messaging/crw_js_window_id_manager.mm
similarity index 95%
rename from ios/web/web_state/js/crw_js_window_id_manager.mm
rename to ios/web/js_messaging/crw_js_window_id_manager.mm
index f0362b7..da49066 100644
--- a/ios/web/web_state/js/crw_js_window_id_manager.mm
+++ b/ios/web/js_messaging/crw_js_window_id_manager.mm
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/js/crw_js_window_id_manager.h"
+#import "ios/web/js_messaging/crw_js_window_id_manager.h"
 
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/sys_string_conversions.h"
 #include "crypto/random.h"
-#import "ios/web/web_state/js/page_script_util.h"
+#import "ios/web/js_messaging/page_script_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/web/web_state/js/crw_js_window_id_manager_unittest.mm b/ios/web/js_messaging/crw_js_window_id_manager_unittest.mm
similarity index 96%
rename from ios/web/web_state/js/crw_js_window_id_manager_unittest.mm
rename to ios/web/js_messaging/crw_js_window_id_manager_unittest.mm
index 2b96e44..43c9960 100644
--- a/ios/web/web_state/js/crw_js_window_id_manager_unittest.mm
+++ b/ios/web/js_messaging/crw_js_window_id_manager_unittest.mm
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/js/crw_js_window_id_manager.h"
+#import "ios/web/js_messaging/crw_js_window_id_manager.h"
 
 #import <WebKit/WebKit.h>
 
+#import "ios/web/js_messaging/page_script_util.h"
 #include "ios/web/public/test/fakes/test_browser_state.h"
 #import "ios/web/public/test/js_test_util.h"
-#import "ios/web/web_state/js/page_script_util.h"
 #import "testing/gtest_mac.h"
 #import "testing/platform_test.h"
 
diff --git a/ios/web/web_state/js/page_script_util.h b/ios/web/js_messaging/page_script_util.h
similarity index 87%
rename from ios/web/web_state/js/page_script_util.h
rename to ios/web/js_messaging/page_script_util.h
index ef37123..6d986fe 100644
--- a/ios/web/web_state/js/page_script_util.h
+++ b/ios/web/js_messaging/page_script_util.h
@@ -2,12 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_WEB_STATE_JS_PAGE_SCRIPT_UTIL_H_
-#define IOS_WEB_WEB_STATE_JS_PAGE_SCRIPT_UTIL_H_
+#ifndef IOS_WEB_JS_MESSAGING_PAGE_SCRIPT_UTIL_H_
+#define IOS_WEB_JS_MESSAGING_PAGE_SCRIPT_UTIL_H_
 
 #import <Foundation/Foundation.h>
 
-
 namespace web {
 
 class BrowserState;
@@ -30,4 +29,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_WEB_STATE_JS_PAGE_SCRIPT_UTIL_H_
+#endif  // IOS_WEB_JS_MESSAGING_PAGE_SCRIPT_UTIL_H_
diff --git a/ios/web/web_state/js/page_script_util.mm b/ios/web/js_messaging/page_script_util.mm
similarity index 96%
rename from ios/web/web_state/js/page_script_util.mm
rename to ios/web/js_messaging/page_script_util.mm
index b6aa8f0e..71f9797 100644
--- a/ios/web/web_state/js/page_script_util.mm
+++ b/ios/web/js_messaging/page_script_util.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/js/page_script_util.h"
+#import "ios/web/js_messaging/page_script_util.h"
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -45,10 +45,9 @@
 // UTF-16 to UTF-8, which can cause problems when injecting scripts depending
 // on the page encoding (see crbug.com/302741).
 NSString* EscapedQuotedString(NSString* string) {
-  string =
-      [string stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
+  string = [string stringByReplacingOccurrencesOfString:@"\\"
+                                             withString:@"\\\\"];
   return [string stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"];
-  ;
 }
 
 }  // namespace
diff --git a/ios/web/web_state/js/page_script_util_unittest.mm b/ios/web/js_messaging/page_script_util_unittest.mm
similarity index 97%
rename from ios/web/web_state/js/page_script_util_unittest.mm
rename to ios/web/js_messaging/page_script_util_unittest.mm
index 8f69a98..ddcadd3 100644
--- a/ios/web/web_state/js/page_script_util_unittest.mm
+++ b/ios/web/js_messaging/page_script_util_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/js/page_script_util.h"
+#import "ios/web/js_messaging/page_script_util.h"
 
 #import <UIKit/UIKit.h>
 #import <WebKit/WebKit.h>
diff --git a/ios/web/web_state/js/resources/message.js b/ios/web/js_messaging/resources/message.js
similarity index 100%
rename from ios/web/web_state/js/resources/message.js
rename to ios/web/js_messaging/resources/message.js
diff --git a/ios/web/web_state/js/resources/setup_frame.js b/ios/web/js_messaging/resources/setup_frame.js
similarity index 100%
rename from ios/web/web_state/js/resources/setup_frame.js
rename to ios/web/js_messaging/resources/setup_frame.js
diff --git a/ios/web/web_state/js/resources/window_id.js b/ios/web/js_messaging/resources/window_id.js
similarity index 100%
rename from ios/web/web_state/js/resources/window_id.js
rename to ios/web/js_messaging/resources/window_id.js
diff --git a/ios/web/web_state/ui/web_view_js_utils.h b/ios/web/js_messaging/web_view_js_utils.h
similarity index 88%
rename from ios/web/web_state/ui/web_view_js_utils.h
rename to ios/web/js_messaging/web_view_js_utils.h
index a0f2b90..d6f08c0 100644
--- a/ios/web/web_state/ui/web_view_js_utils.h
+++ b/ios/web/js_messaging/web_view_js_utils.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_WEB_STATE_UI_WEB_VIEW_JS_UTILS_H_
-#define IOS_WEB_WEB_STATE_UI_WEB_VIEW_JS_UTILS_H_
+#ifndef IOS_WEB_JS_MESSAGING_WEB_VIEW_JS_UTILS_H_
+#define IOS_WEB_JS_MESSAGING_WEB_VIEW_JS_UTILS_H_
 
 #import <Foundation/Foundation.h>
 #include <memory>
@@ -41,4 +41,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_WEB_STATE_UI_WEB_VIEW_JS_UTILS_H_
+#endif  // IOS_WEB_JS_MESSAGING_WEB_VIEW_JS_UTILS_H_
diff --git a/ios/web/web_state/ui/web_view_js_utils.mm b/ios/web/js_messaging/web_view_js_utils.mm
similarity index 98%
rename from ios/web/web_state/ui/web_view_js_utils.mm
rename to ios/web/js_messaging/web_view_js_utils.mm
index f38950c..5ba2f04 100644
--- a/ios/web/web_state/ui/web_view_js_utils.mm
+++ b/ios/web/js_messaging/web_view_js_utils.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/ui/web_view_js_utils.h"
+#import "ios/web/js_messaging/web_view_js_utils.h"
 
 #include <CoreFoundation/CoreFoundation.h>
 #import <WebKit/WebKit.h>
diff --git a/ios/web/navigation/BUILD.gn b/ios/web/navigation/BUILD.gn
index 559fd7e..3980511d 100644
--- a/ios/web/navigation/BUILD.gn
+++ b/ios/web/navigation/BUILD.gn
@@ -20,6 +20,7 @@
     "//ios/net",
     "//ios/web:core",
     "//ios/web/common",
+    "//ios/web/js_messaging",
     "//ios/web/navigation:wk_navigation_util",
     "//ios/web/public",
     "//ios/web/public/deprecated",
@@ -31,6 +32,7 @@
     "//ios/web/web_state:user_interaction",
     "//ios/web/web_state:web_state_impl_header",
     "//ios/web/web_state/ui:crw_web_view_navigation_proxy",
+    "//ios/web/web_state/ui/controller",
     "//ios/web/web_view:util",
     "//ui/base",
     "//url",
diff --git a/ios/web/navigation/crw_wk_navigation_handler.h b/ios/web/navigation/crw_wk_navigation_handler.h
index e638e3b..fde4e50 100644
--- a/ios/web/navigation/crw_wk_navigation_handler.h
+++ b/ios/web/navigation/crw_wk_navigation_handler.h
@@ -16,15 +16,17 @@
 @class CRWWKNavigationHandler;
 @class CRWPendingNavigationInfo;
 @class CRWWKNavigationStates;
+@class CRWJSInjector;
+@class CRWLegacyNativeContentController;
+@class CRWCertVerificationController;
 class GURL;
-namespace base {
-class RepeatingTimer;
-}
 namespace web {
 enum class WKNavigationState;
+enum class ErrorRetryCommand;
 struct Referrer;
 class WebStateImpl;
 class NavigationContextImpl;
+class NavigationItemImpl;
 class UserInteractionState;
 class WKBackForwardListItemHolder;
 }
@@ -45,6 +47,20 @@
     certVerificationErrorsForNavigationHandler:
         (CRWWKNavigationHandler*)navigationHandler;
 
+// Returns associated certificate verificatio controller.
+- (CRWCertVerificationController*)
+    certVerificationControllerForNavigationHandler:
+        (CRWWKNavigationHandler*)navigationHandler;
+
+// Returns the associated js injector.
+- (CRWJSInjector*)JSInjectorForNavigationHandler:
+    (CRWWKNavigationHandler*)navigationHandler;
+
+// Returns the associated legacy native content controller.
+- (CRWLegacyNativeContentController*)
+    legacyNativeContentControllerForNavigationHandler:
+        (CRWWKNavigationHandler*)navigationHandler;
+
 // Returns YES if WKWebView is halted.
 - (BOOL)navigationHandlerWebViewIsHalted:
     (CRWWKNavigationHandler*)navigationHandler;
@@ -117,6 +133,49 @@
 - (void)navigationHandlerRemoveAllWebFrames:
     (CRWWKNavigationHandler*)navigationHandler;
 
+// Instructs the delegate to display the webView.
+- (void)navigationHandlerDisplayWebView:
+    (CRWWKNavigationHandler*)navigationHandler;
+
+// Resets any state that is associated with a specific document object (e.g.,
+// page interaction tracking).
+- (void)navigationHandlerResetDocumentSpecificState:
+    (CRWWKNavigationHandler*)navigationHandler;
+
+// Notifies the delegate that the page has actually started loading.
+- (void)navigationHandlerDidStartLoading:
+    (CRWWKNavigationHandler*)navigationHandler;
+
+// Notifies the delegate that the web page has changed document and/or URL.
+- (void)navigationHandler:(CRWWKNavigationHandler*)navigationHandler
+    didChangePageWithContext:(web::NavigationContextImpl*)context;
+
+// Instructs the delegate to update the SSL status for the current navigation
+// item.
+- (void)navigationHandlerUpdateSSLStatusForCurrentNavigationItem:
+    (CRWWKNavigationHandler*)navigationHandler;
+
+// Instructs the delegate to update the HTML5 history state of the page using
+// the current NavigationItem.
+- (void)navigationHandlerUpdateHTML5HistoryState:
+    (CRWWKNavigationHandler*)navigationHandler;
+
+// Instructs the delegate to execute the command specified by the
+// ErrorRetryStateMachine.
+- (void)navigationHandler:(CRWWKNavigationHandler*)navigationHandler
+    handleErrorRetryCommand:(web::ErrorRetryCommand)command
+             navigationItem:(web::NavigationItemImpl*)item
+          navigationContext:(web::NavigationContextImpl*)context
+         originalNavigation:(WKNavigation*)originalNavigation;
+
+// Notifies the delegate that navigation has finished.
+- (void)navigationHandler:(CRWWKNavigationHandler*)navigationHandler
+      didFinishNavigation:(web::NavigationContextImpl*)context;
+
+// Notifies the delegate that web process has crashed.
+- (void)navigationHandlerWebProcessDidCrash:
+    (CRWWKNavigationHandler*)navigationHandler;
+
 @end
 
 // Handler class for WKNavigationDelegate, deals with navigation callbacks from
@@ -143,11 +202,6 @@
 // TODO(crbug.com/956511): Remove this once refactor is done.
 @property(nonatomic, readwrite, assign) web::WKNavigationState navigationState;
 
-// The SafeBrowsingDetection timer.
-// TODO(crbug.com/956511): Remove this once refactor is done.
-@property(nonatomic, readonly, assign)
-    base::RepeatingTimer* safeBrowsingWarningDetectionTimer;
-
 // Returns the WKBackForwardlistItemHolder of current navigation item.
 @property(nonatomic, readonly, assign)
     web::WKBackForwardListItemHolder* currentBackForwardListItemHolder;
@@ -155,11 +209,6 @@
 // Returns the referrer for the current page.
 @property(nonatomic, readonly, assign) web::Referrer currentReferrer;
 
-// Discards non committed items, only if the last committed URL was not loaded
-// in native view. But if it was a native view, no discard will happen to avoid
-// an ugly animation where the web view is inserted and quickly removed.
-- (void)discardNonCommittedItemsIfLastCommittedWasNotNativeView;
-
 // Instructs this handler to stop loading.
 - (void)stopLoading;
 
@@ -171,12 +220,6 @@
 - (web::NavigationContextImpl*)contextForPendingMainFrameNavigationWithURL:
     (const GURL&)URL;
 
-// Notifies that server redirect has been received.
-// TODO(crbug.com/956511): Remove this once "webView:didCommitNavigation" is
-// moved into CRWWKNavigationHandler.
-- (void)didReceiveRedirectForNavigation:(web::NavigationContextImpl*)context
-                                withURL:(const GURL&)URL;
-
 // Returns YES if current navigation item is WKNavigationTypeBackForward.
 - (BOOL)isCurrentNavigationBackForward;
 
@@ -184,20 +227,9 @@
 // loaded by a POST request.
 - (BOOL)isCurrentNavigationItemPOST;
 
-// Updates current state with any pending information. Should be called when a
-// navigation is committed.
-// TODO(crbug.com/956511): Make this private once "webView:didCommitNavigation"
-// is moved into CRWWKNavigationHandler.
-- (void)commitPendingNavigationInfoInWebView:(WKWebView*)webView;
-
-// WKNavigation objects are used as a weak key to store web::NavigationContext.
-// WKWebView manages WKNavigation lifetime and destroys them after the
-// navigation is finished. However for window opening navigations WKWebView
-// passes null WKNavigation to WKNavigationDelegate callbacks and strong key is
-// used to store web::NavigationContext. Those "null" navigations have to be
-// cleaned up manually by calling this method.
-// TODO(crbug.com/956511): Make this private once refactor is done.
-- (void)forgetNullWKNavigation:(WKNavigation*)navigation;
+// Sets last committed NavigationItem's title to the given |title|, which can
+// not be nil.
+- (void)setLastCommittedNavigationItemTitle:(NSString*)title;
 
 @end
 
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm
index 8cfd83b..9a8af30 100644
--- a/ios/web/navigation/crw_wk_navigation_handler.mm
+++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -10,8 +10,13 @@
 #include "base/timer/timer.h"
 #import "ios/net/http_response_headers_util.h"
 #include "ios/web/common/features.h"
+#import "ios/web/js_messaging/crw_js_injector.h"
+#import "ios/web/js_messaging/web_frames_manager_impl.h"
+#import "ios/web/js_messaging/web_view_js_utils.h"
 #import "ios/web/navigation/crw_pending_navigation_info.h"
+#import "ios/web/navigation/crw_session_controller.h"
 #import "ios/web/navigation/crw_wk_navigation_states.h"
+#include "ios/web/navigation/error_retry_state_machine.h"
 #import "ios/web/navigation/navigation_context_impl.h"
 #import "ios/web/navigation/navigation_manager_impl.h"
 #include "ios/web/navigation/navigation_manager_util.h"
@@ -24,11 +29,15 @@
 #import "ios/web/public/download/download_controller.h"
 #import "ios/web/public/url_scheme_util.h"
 #import "ios/web/public/web_client.h"
+#import "ios/web/security/crw_cert_verification_controller.h"
 #import "ios/web/security/wk_web_view_security_util.h"
+#import "ios/web/web_state/ui/controller/crw_legacy_native_content_controller.h"
 #import "ios/web/web_state/user_interaction_state.h"
 #import "ios/web/web_state/web_state_impl.h"
+#include "ios/web/web_view/content_type_util.h"
 #import "ios/web/web_view/wk_web_view_util.h"
 #import "net/base/mac/url_conversions.h"
+#include "net/cert/x509_util_ios.h"
 #include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -37,6 +46,7 @@
 
 using web::wk_navigation_util::IsPlaceholderUrl;
 using web::wk_navigation_util::CreatePlaceholderUrlForUrl;
+using web::wk_navigation_util::ExtractUrlFromPlaceholderUrl;
 using web::wk_navigation_util::kReferrerHeaderName;
 using web::wk_navigation_util::IsRestoreSessionUrl;
 using web::wk_navigation_util::IsWKInternalUrl;
@@ -68,6 +78,17 @@
 // Returns the CertVerificationErrorsCacheType from self.delegate.
 @property(nonatomic, readonly, assign)
     web::CertVerificationErrorsCacheType* certVerificationErrors;
+// Returns the CRWCertVerificationController from self.delegate.
+@property(nonatomic, readonly, weak)
+    CRWCertVerificationController* certVerificationController;
+// Returns the docuemnt URL from self.delegate.
+@property(nonatomic, readonly, assign) GURL documentURL;
+// Returns the session controller from self.navigationManagerImpl.
+@property(nonatomic, readonly, weak) CRWSessionController* sessionController;
+// Returns the js injector from self.delegate.
+@property(nonatomic, readonly, weak) CRWJSInjector* JSInjector;
+@property(nonatomic, readonly, weak)
+    CRWLegacyNativeContentController* legacyNativeContentController;
 
 @end
 
@@ -266,8 +287,7 @@
           self.webStateImpl->GetNavigationManager()->GetLastCommittedItem();
       if (lastCommittedItem) {
         GURL lastCommittedURL = lastCommittedItem->GetURL();
-        if (lastCommittedURL !=
-            [self.delegate navigationHandlerDocumentURL:self]) {
+        if (lastCommittedURL != self.documentURL) {
           [self.delegate navigationHandlerRequirePageReconstruction:self];
           [self.delegate navigationHandler:self
                             setDocumentURL:lastCommittedURL
@@ -533,7 +553,7 @@
                          WKNavigationTypeBackForward;
     bool isRestoringSession =
         web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
-        IsRestoreSessionUrl([self.delegate navigationHandlerDocumentURL:self]);
+        IsRestoreSessionUrl(self.documentURL);
     exemptedAppSpecificLoad =
         currentItemIsPlaceholder || isBackForward || isRestoringSession;
   }
@@ -553,8 +573,7 @@
     // pages. WebUI pages may have increased power and using the same web
     // process (which may potentially be controller by an attacker) is
     // dangerous.
-    if (web::GetWebClient()->IsAppSpecificURL(
-            [self.delegate navigationHandlerDocumentURL:self])) {
+    if (web::GetWebClient()->IsAppSpecificURL(self.documentURL)) {
       [self.delegate navigationHandlerAbortLoading:self];
       web::NavigationManager::WebLoadParams params(webViewURL);
       self.navigationManagerImpl->LoadURLWithParams(params);
@@ -682,17 +701,384 @@
 - (void)webView:(WKWebView*)webView
     didCommitNavigation:(WKNavigation*)navigation {
   [self didReceiveWKNavigationDelegateCallback];
+
+  // For reasons not yet fully understood, sometimes WKWebView triggers
+  // |webView:didFinishNavigation| before |webView:didCommitNavigation|. If a
+  // navigation is already finished, stop processing
+  // (https://crbug.com/818796#c2).
+  if ([self.navigationStates stateForNavigation:navigation] ==
+      web::WKNavigationState::FINISHED)
+    return;
+
+  BOOL committedNavigation =
+      [self.navigationStates isCommittedNavigation:navigation];
+  if (!web::features::StorePendingItemInContext()) {
+    // Code in this method relies on existance of pending item.
+    [self.navigationStates setState:web::WKNavigationState::COMMITTED
+                      forNavigation:navigation];
+  }
+
+  self.certVerificationErrors->Clear();
+
+  // Invariant: Every |navigation| should have a |context|. Note that violation
+  // of this invariant is currently observed in production, but the cause is not
+  // well understood. This DCHECK is meant to catch such cases in testing if
+  // they arise.
+  // TODO(crbug.com/864769): Remove nullptr checks on |context| in this method
+  // once the root cause of the invariant violation is found.
+  web::NavigationContextImpl* context =
+      [self.navigationStates contextForNavigation:navigation];
+  DCHECK(context);
+  UMA_HISTOGRAM_BOOLEAN("IOS.CommittedNavigationHasContext", context);
+
+  GURL webViewURL = net::GURLWithNSURL(webView.URL);
+  GURL currentWKItemURL =
+      net::GURLWithNSURL(webView.backForwardList.currentItem.URL);
+  UMA_HISTOGRAM_BOOLEAN("IOS.CommittedURLMatchesCurrentItem",
+                        webViewURL == currentWKItemURL);
+
+  // TODO(crbug.com/787497): Always use webView.backForwardList.currentItem.URL
+  // to obtain lastCommittedURL once loadHTML: is no longer user for WebUI.
+  if (webViewURL.is_empty()) {
+    // It is possible for |webView.URL| to be nil, in which case
+    // webView.backForwardList.currentItem.URL will return the right committed
+    // URL (crbug.com/784480).
+    webViewURL = currentWKItemURL;
+  } else if (context && !context->IsPlaceholderNavigation() &&
+             context->GetUrl() == currentWKItemURL) {
+    // If webView.backForwardList.currentItem.URL matches |context|, then this
+    // is a known edge case where |webView.URL| is wrong.
+    // TODO(crbug.com/826013): Remove this workaround.
+    webViewURL = currentWKItemURL;
+  }
+
+  if (self.pendingNavigationInfo.MIMEType)
+    context->SetMimeType(self.pendingNavigationInfo.MIMEType);
+
+  // Don't show webview for placeholder navigation to avoid covering the native
+  // content, which may have already been shown.
+  if (!IsPlaceholderUrl(webViewURL))
+    [self.delegate navigationHandlerDisplayWebView:self];
+
+  // Update HTTP response headers.
+  self.webStateImpl->UpdateHttpResponseHeaders(webViewURL);
+
+  if (@available(iOS 11.3, *)) {
+    // On iOS 11.3 didReceiveServerRedirectForProvisionalNavigation: is not
+    // always called. So if URL was unexpectedly changed then it's probably
+    // because redirect callback was not called.
+    if (@available(iOS 12, *)) {
+      // rdar://37547029 was fixed on iOS 12.
+    } else if (context && !context->IsPlaceholderNavigation() &&
+               context->GetUrl() != webViewURL) {
+      [self didReceiveRedirectForNavigation:context withURL:webViewURL];
+    }
+  }
+
+  // |context| will be nil if this navigation has been already committed and
+  // finished.
+  if (context) {
+    web::NavigationManager* navigationManager =
+        self.webStateImpl->GetNavigationManager();
+    GURL pendingURL;
+    if (web::features::StorePendingItemInContext() &&
+        navigationManager->GetPendingItemIndex() == -1) {
+      if (context->GetItem()) {
+        pendingURL = context->GetItem()->GetURL();
+      }
+    } else {
+      if (navigationManager->GetPendingItem()) {
+        pendingURL = navigationManager->GetPendingItem()->GetURL();
+      }
+    }
+    if ((pendingURL == webViewURL) || (context->IsLoadingHtmlString()) ||
+        (!web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
+         ui::PageTransitionCoreTypeIs(context->GetPageTransition(),
+                                      ui::PAGE_TRANSITION_RELOAD) &&
+         navigationManager->GetLastCommittedItem())) {
+      // Commit navigation if at least one of these is true:
+      //  - Navigation has pending item (this should always be true, but
+      //    pending item may not exist due to crbug.com/925304).
+      //  - Navigation is loadHTMLString:baseURL: navigation, which does not
+      //    create a pending item, but modifies committed item instead.
+      //  - Transition type is reload with Legacy Navigation Manager (Legacy
+      //    Navigation Manager does not create pending item for reload due to
+      //    crbug.com/676129)
+      context->SetHasCommitted(true);
+    }
+    context->SetResponseHeaders(self.webStateImpl->GetHttpResponseHeaders());
+    self.webStateImpl->SetContentsMimeType(
+        base::SysNSStringToUTF8(context->GetMimeType()));
+  }
+
+  [self commitPendingNavigationInfoInWebView:webView];
+
+  [self.delegate navigationHandlerRemoveAllWebFrames:self];
+
+  // This point should closely approximate the document object change, so reset
+  // the list of injected scripts to those that are automatically injected.
+  // Do not inject window ID if this is a placeholder URL: window ID is not
+  // needed for native view. For WebUI, let the window ID be injected when the
+  // |loadHTMLString:baseURL| navigation is committed.
+  if (!web::GetWebClient()->IsSlimNavigationManagerEnabled() ||
+      !IsPlaceholderUrl(webViewURL)) {
+    [self.JSInjector resetInjectedScriptSet];
+
+    const std::string& mime_type = self.webStateImpl->GetContentsMimeType();
+    if (web::IsContentTypeHtml(mime_type) ||
+        web::IsContentTypeImage(mime_type) || mime_type.empty()) {
+      // In unit tests MIME type will be empty, because loadHTML:forURL: does
+      // not notify web view delegate about received response, so web controller
+      // does not get a chance to properly update MIME type.
+      [self.JSInjector injectWindowID];
+      web::WebFramesManagerImpl::FromWebState(self.webStateImpl)
+          ->RegisterExistingFrames();
+    }
+  }
+
+  if (committedNavigation) {
+    // WKWebView called didCommitNavigation: with incorrect WKNavigation object.
+    // Correct WKNavigation object for this navigation was deallocated because
+    // WKWebView mistakenly cancelled the navigation and called
+    // didFailProvisionalNavigation. As a result web::NavigationContext for this
+    // navigation does not exist anymore. Find correct navigation item and make
+    // it committed.
+    if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+      bool found_correct_navigation_item = false;
+      for (size_t i = 0; i < self.sessionController.items.size(); i++) {
+        web::NavigationItem* item = self.sessionController.items[i].get();
+        found_correct_navigation_item = item->GetURL() == webViewURL;
+        if (found_correct_navigation_item) {
+          [self.sessionController goToItemAtIndex:i
+                         discardNonCommittedItems:NO];
+          break;
+        }
+      }
+      DCHECK(found_correct_navigation_item);
+    }
+    [self.delegate navigationHandlerResetDocumentSpecificState:self];
+    [self.delegate navigationHandlerDidStartLoading:self];
+  } else if (context) {
+    // If |navigation| is nil (which happens for windows open by DOM), then it
+    // should be the first and the only pending navigation.
+    BOOL isLastNavigation =
+        !navigation ||
+        [[self.navigationStates lastAddedNavigation] isEqual:navigation];
+    if (isLastNavigation ||
+        (web::features::StorePendingItemInContext() &&
+         self.navigationManagerImpl->GetPendingItemIndex() == -1)) {
+      [self.delegate navigationHandler:self didChangePageWithContext:context];
+    } else if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+      // WKWebView has more than one in progress navigation, and committed
+      // navigation was not the latest. Change last committed item to one that
+      // corresponds to committed navigation.
+      int itemIndex = web::GetCommittedItemIndexWithUniqueID(
+          self.navigationManagerImpl, context->GetNavigationItemUniqueID());
+      // Do not discard pending entry, because another pending navigation is
+      // still in progress and will commit or fail soon.
+      [self.sessionController goToItemAtIndex:itemIndex
+                     discardNonCommittedItems:NO];
+    }
+  }
+
+  if (web::features::StorePendingItemInContext()) {
+    // No further code relies an existance of pending item, so this navigation
+    // can be marked as "committed".
+    [self.navigationStates setState:web::WKNavigationState::COMMITTED
+                      forNavigation:navigation];
+  }
+
+  // This is the point where the document's URL has actually changed.
+  [self.delegate navigationHandler:self
+                    setDocumentURL:webViewURL
+                           context:context];
+
+  if (!committedNavigation && context && !context->IsLoadingErrorPage()) {
+    self.webStateImpl->OnNavigationFinished(context);
+  }
+
+  // Do not update the HTML5 history state or states of the last committed item
+  // for placeholder page because the actual navigation item will not be
+  // committed until the native content or WebUI is shown.
+  if (context && !context->IsPlaceholderNavigation() &&
+      !context->IsLoadingErrorPage() &&
+      !context->GetUrl().SchemeIs(url::kAboutScheme) &&
+      !IsRestoreSessionUrl(context->GetUrl())) {
+    [self.delegate
+        navigationHandlerUpdateSSLStatusForCurrentNavigationItem:self];
+    [self.delegate navigationHandlerUpdateHTML5HistoryState:self];
+    if (!context->IsLoadingErrorPage() && !IsRestoreSessionUrl(webViewURL)) {
+      [self setLastCommittedNavigationItemTitle:webView.title];
+    }
+  }
+
+  // Report cases where SSL cert is missing for a secure connection.
+  if (self.documentURL.SchemeIsCryptographic()) {
+    scoped_refptr<net::X509Certificate> cert;
+    cert = web::CreateCertFromTrust(webView.serverTrust);
+    UMA_HISTOGRAM_BOOLEAN("WebController.WKWebViewHasCertForSecureConnection",
+                          static_cast<bool>(cert));
+  }
 }
 
 - (void)webView:(WKWebView*)webView
     didFinishNavigation:(WKNavigation*)navigation {
   [self didReceiveWKNavigationDelegateCallback];
+
+  // Sometimes |webView:didFinishNavigation| arrives before
+  // |webView:didCommitNavigation|. Explicitly trigger post-commit processing.
+  bool navigationCommitted =
+      [self.navigationStates stateForNavigation:navigation] ==
+      web::WKNavigationState::COMMITTED;
+  UMA_HISTOGRAM_BOOLEAN("IOS.WKWebViewFinishBeforeCommit",
+                        !navigationCommitted);
+  if (!navigationCommitted) {
+    [self webView:webView didCommitNavigation:navigation];
+    DCHECK_EQ(web::WKNavigationState::COMMITTED,
+              [self.navigationStates stateForNavigation:navigation]);
+  }
+
+  // Sometimes |didFinishNavigation| callback arrives after |stopLoading| has
+  // been called. Abort in this case.
+  if ([self.navigationStates stateForNavigation:navigation] ==
+      web::WKNavigationState::NONE) {
+    return;
+  }
+
+  GURL webViewURL = net::GURLWithNSURL(webView.URL);
+  GURL currentWKItemURL =
+      net::GURLWithNSURL(webView.backForwardList.currentItem.URL);
+  UMA_HISTOGRAM_BOOLEAN("IOS.FinishedURLMatchesCurrentItem",
+                        webViewURL == currentWKItemURL);
+
+  web::NavigationContextImpl* context =
+      [self.navigationStates contextForNavigation:navigation];
+  web::NavigationItemImpl* item =
+      context ? web::GetItemWithUniqueID(self.navigationManagerImpl, context)
+              : nullptr;
+
+  // Invariant: every |navigation| should have a |context| and a |item|.
+  // TODO(crbug.com/899383) Fix invariant violation when a new pending item is
+  // created before a placeholder load finishes.
+  if (IsPlaceholderUrl(webViewURL)) {
+    GURL originalURL = ExtractUrlFromPlaceholderUrl(webViewURL);
+    if (self.currentNavItem != item &&
+        self.currentNavItem->GetVirtualURL() != originalURL) {
+      // The |didFinishNavigation| callback for placeholder navigation can
+      // arrive after another navigation has started. Abort in this case.
+      return;
+    }
+  }
+  DCHECK(context);
+  DCHECK(item);
+  UMA_HISTOGRAM_BOOLEAN("IOS.FinishedNavigationHasContext", context);
+  UMA_HISTOGRAM_BOOLEAN("IOS.FinishedNavigationHasItem", item);
+
+  // TODO(crbug.com/864769): Remove this guard after fixing root cause of
+  // invariant violation in production.
+  if (context && item) {
+    GURL navigationURL = context->IsPlaceholderNavigation()
+                             ? CreatePlaceholderUrlForUrl(context->GetUrl())
+                             : context->GetUrl();
+    if (navigationURL == currentWKItemURL) {
+      // If webView.backForwardList.currentItem.URL matches |context|, then this
+      // is a known edge case where |webView.URL| is wrong.
+      // TODO(crbug.com/826013): Remove this workaround.
+      webViewURL = currentWKItemURL;
+    }
+
+    if (!IsWKInternalUrl(currentWKItemURL) && currentWKItemURL == webViewURL &&
+        currentWKItemURL != context->GetUrl() &&
+        item == self.navigationManagerImpl->GetLastCommittedItem() &&
+        item->GetURL().GetOrigin() == currentWKItemURL.GetOrigin()) {
+      // WKWebView sometimes changes URL on the same navigation, likely due to
+      // location.replace() or history.replaceState in onload handler that does
+      // not change the origin. It's safe to update |item| and |context| URL
+      // because they are both associated to WKNavigation*, which is a stable ID
+      // for the navigation. See https://crbug.com/869540 for a real-world case.
+      item->SetURL(currentWKItemURL);
+      context->SetUrl(currentWKItemURL);
+    }
+
+    if (IsPlaceholderUrl(webViewURL)) {
+      if (item->GetURL() == webViewURL) {
+        // Current navigation item is restored from a placeholder URL as part
+        // of session restoration. It is now safe to update the navigation
+        // item URL to the original app-specific URL.
+        item->SetURL(ExtractUrlFromPlaceholderUrl(webViewURL));
+      }
+
+      if ([self.legacyNativeContentController
+              shouldLoadURLInNativeView:item->GetURL()]) {
+        [self.legacyNativeContentController
+            webViewDidFinishNavigationWithContext:context
+                                          andItem:item];
+      } else if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
+                 item->error_retry_state_machine().state() ==
+                     web::ErrorRetryState::kNoNavigationError) {
+        // Offline pages can leave the WKBackForwardList current item as a
+        // placeholder with no saved content.  In this case, trigger a retry
+        // on that navigation with an update |item| url and |context| error.
+        item->SetURL(
+            ExtractUrlFromPlaceholderUrl(net::GURLWithNSURL(webView.URL)));
+        item->SetVirtualURL(item->GetURL());
+        context->SetError([NSError
+            errorWithDomain:NSURLErrorDomain
+                       code:NSURLErrorNetworkConnectionLost
+                   userInfo:@{
+                     NSURLErrorFailingURLStringErrorKey :
+                         base::SysUTF8ToNSString(item->GetURL().spec())
+                   }]);
+        item->error_retry_state_machine().SetRetryPlaceholderNavigation();
+      }
+    }
+
+    web::ErrorRetryCommand command =
+        item->error_retry_state_machine().DidFinishNavigation(webViewURL);
+    [self.delegate navigationHandler:self
+             handleErrorRetryCommand:command
+                      navigationItem:item
+                   navigationContext:context
+                  originalNavigation:navigation];
+  }
+
+  [self.navigationStates setState:web::WKNavigationState::FINISHED
+                    forNavigation:navigation];
+
+  DCHECK(![self.delegate navigationHandlerWebViewIsHalted:self]);
+  // Trigger JavaScript driven post-document-load-completion tasks.
+  // TODO(crbug.com/546350): Investigate using
+  // WKUserScriptInjectionTimeAtDocumentEnd to inject this material at the
+  // appropriate time rather than invoking here.
+  web::ExecuteJavaScript(webView, @"__gCrWeb.didFinishNavigation()", nil);
+  [self.delegate navigationHandler:self didFinishNavigation:context];
+
+  if (web::features::StorePendingItemInContext()) {
+    // Remove the navigation to immediately get rid of pending item.
+    if (web::WKNavigationState::NONE !=
+        [self.navigationStates stateForNavigation:navigation]) {
+      [self.navigationStates removeNavigation:navigation];
+    }
+  } else {
+    [self forgetNullWKNavigation:navigation];
+  }
 }
 
 - (void)webView:(WKWebView*)webView
     didFailNavigation:(WKNavigation*)navigation
             withError:(NSError*)error {
   [self didReceiveWKNavigationDelegateCallback];
+
+  [self.navigationStates setState:web::WKNavigationState::FAILED
+                    forNavigation:navigation];
+
+  [self.delegate navigationHandler:self
+                   handleLoadError:error
+                     forNavigation:navigation
+                   provisionalLoad:NO];
+  [self.delegate navigationHandlerRemoveAllWebFrames:self];
+  self.certVerificationErrors->Clear();
+  [self forgetNullWKNavigation:navigation];
 }
 
 - (void)webView:(WKWebView*)webView
@@ -701,10 +1087,50 @@
                         (void (^)(NSURLSessionAuthChallengeDisposition,
                                   NSURLCredential*))completionHandler {
   [self didReceiveWKNavigationDelegateCallback];
+
+  NSString* authMethod = challenge.protectionSpace.authenticationMethod;
+  if ([authMethod isEqual:NSURLAuthenticationMethodHTTPBasic] ||
+      [authMethod isEqual:NSURLAuthenticationMethodNTLM] ||
+      [authMethod isEqual:NSURLAuthenticationMethodHTTPDigest]) {
+    [self handleHTTPAuthForChallenge:challenge
+                   completionHandler:completionHandler];
+    return;
+  }
+
+  if (![authMethod isEqual:NSURLAuthenticationMethodServerTrust]) {
+    completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
+    return;
+  }
+
+  SecTrustRef trust = challenge.protectionSpace.serverTrust;
+  base::ScopedCFTypeRef<SecTrustRef> scopedTrust(trust,
+                                                 base::scoped_policy::RETAIN);
+  __weak CRWWKNavigationHandler* weakSelf = self;
+  [self.certVerificationController
+      decideLoadPolicyForTrust:scopedTrust
+                          host:challenge.protectionSpace.host
+             completionHandler:^(web::CertAcceptPolicy policy,
+                                 net::CertStatus status) {
+               CRWWKNavigationHandler* strongSelf = weakSelf;
+               if (!strongSelf) {
+                 completionHandler(
+                     NSURLSessionAuthChallengeRejectProtectionSpace, nil);
+                 return;
+               }
+               [strongSelf processAuthChallenge:challenge
+                            forCertAcceptPolicy:policy
+                                     certStatus:status
+                              completionHandler:completionHandler];
+             }];
 }
 
 - (void)webViewWebContentProcessDidTerminate:(WKWebView*)webView {
   [self didReceiveWKNavigationDelegateCallback];
+
+  self.certVerificationErrors->Clear();
+  self.webProcessCrashed = YES;
+
+  [self.delegate navigationHandlerWebProcessDidCrash:self];
 }
 
 #pragma mark - Private methods
@@ -725,12 +1151,34 @@
   return [self.delegate certVerificationErrorsForNavigationHandler:self];
 }
 
+- (CRWJSInjector*)JSInjector {
+  return [self.delegate JSInjectorForNavigationHandler:self];
+}
+
+- (CRWCertVerificationController*)certVerificationController {
+  return [self.delegate certVerificationControllerForNavigationHandler:self];
+}
+
+- (CRWLegacyNativeContentController*)legacyNativeContentController {
+  return [self.delegate legacyNativeContentControllerForNavigationHandler:self];
+}
+
+- (GURL)documentURL {
+  return [self.delegate navigationHandlerDocumentURL:self];
+}
+
 - (web::NavigationItemImpl*)currentNavItem {
   return self.navigationManagerImpl
              ? self.navigationManagerImpl->GetCurrentItemImpl()
              : nullptr;
 }
 
+- (CRWSessionController*)sessionController {
+  return self.navigationManagerImpl
+             ? self.navigationManagerImpl->GetSessionController()
+             : nil;
+}
+
 // This method should be called on receiving WKNavigationDelegate callbacks. It
 // will log a metric if the callback occurs after the reciever has already been
 // closed. It also stops the SafeBrowsing warning detection timer, since after
@@ -815,7 +1263,7 @@
   }
 
   // If the session is being restored, allow the navigation.
-  if (IsRestoreSessionUrl([self.delegate navigationHandlerDocumentURL:self])) {
+  if (IsRestoreSessionUrl(self.documentURL)) {
     return YES;
   }
 
@@ -998,6 +1446,100 @@
   self.userInteractionState->ResetLastTransferTime();
 }
 
+// WKNavigation objects are used as a weak key to store web::NavigationContext.
+// WKWebView manages WKNavigation lifetime and destroys them after the
+// navigation is finished. However for window opening navigations WKWebView
+// passes null WKNavigation to WKNavigationDelegate callbacks and strong key is
+// used to store web::NavigationContext. Those "null" navigations have to be
+// cleaned up manually by calling this method.
+- (void)forgetNullWKNavigation:(WKNavigation*)navigation {
+  if (!navigation)
+    [self.navigationStates removeNavigation:navigation];
+}
+
+#pragma mark - Auth Challenge
+
+// Used in webView:didReceiveAuthenticationChallenge:completionHandler: to
+// reply with NSURLSessionAuthChallengeDisposition and credentials.
+- (void)processAuthChallenge:(NSURLAuthenticationChallenge*)challenge
+         forCertAcceptPolicy:(web::CertAcceptPolicy)policy
+                  certStatus:(net::CertStatus)certStatus
+           completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition,
+                                       NSURLCredential*))completionHandler {
+  SecTrustRef trust = challenge.protectionSpace.serverTrust;
+  if (policy == web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER) {
+    // Cert is invalid, but user agreed to proceed, override default behavior.
+    completionHandler(NSURLSessionAuthChallengeUseCredential,
+                      [NSURLCredential credentialForTrust:trust]);
+    return;
+  }
+
+  if (policy != web::CERT_ACCEPT_POLICY_ALLOW &&
+      SecTrustGetCertificateCount(trust)) {
+    // The cert is invalid and the user has not agreed to proceed. Cache the
+    // cert verification result in |_certVerificationErrors|, so that it can
+    // later be reused inside |didFailProvisionalNavigation:|.
+    // The leaf cert is used as the key, because the chain provided by
+    // |didFailProvisionalNavigation:| will differ (it is the server-supplied
+    // chain), thus if intermediates were considered, the keys would mismatch.
+    scoped_refptr<net::X509Certificate> leafCert =
+        net::x509_util::CreateX509CertificateFromSecCertificate(
+            SecTrustGetCertificateAtIndex(trust, 0),
+            std::vector<SecCertificateRef>());
+    if (leafCert) {
+      bool is_recoverable =
+          policy == web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_UNDECIDED_BY_USER;
+      std::string host =
+          base::SysNSStringToUTF8(challenge.protectionSpace.host);
+      self.certVerificationErrors->Put(
+          web::CertHostPair(leafCert, host),
+          web::CertVerificationError(is_recoverable, certStatus));
+    }
+  }
+  completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
+}
+
+// Used in webView:didReceiveAuthenticationChallenge:completionHandler: to reply
+// with NSURLSessionAuthChallengeDisposition and credentials.
+- (void)handleHTTPAuthForChallenge:(NSURLAuthenticationChallenge*)challenge
+                 completionHandler:
+                     (void (^)(NSURLSessionAuthChallengeDisposition,
+                               NSURLCredential*))completionHandler {
+  NSURLProtectionSpace* space = challenge.protectionSpace;
+  DCHECK(
+      [space.authenticationMethod isEqual:NSURLAuthenticationMethodHTTPBasic] ||
+      [space.authenticationMethod isEqual:NSURLAuthenticationMethodNTLM] ||
+      [space.authenticationMethod isEqual:NSURLAuthenticationMethodHTTPDigest]);
+
+  self.webStateImpl->OnAuthRequired(
+      space, challenge.proposedCredential,
+      base::BindRepeating(^(NSString* user, NSString* password) {
+        [CRWWKNavigationHandler processHTTPAuthForUser:user
+                                              password:password
+                                     completionHandler:completionHandler];
+      }));
+}
+
+// Used in webView:didReceiveAuthenticationChallenge:completionHandler: to reply
+// with NSURLSessionAuthChallengeDisposition and credentials.
++ (void)processHTTPAuthForUser:(NSString*)user
+                      password:(NSString*)password
+             completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition,
+                                         NSURLCredential*))completionHandler {
+  DCHECK_EQ(user == nil, password == nil);
+  if (!user || !password) {
+    // Embedder cancelled authentication.
+    completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
+    return;
+  }
+  completionHandler(
+      NSURLSessionAuthChallengeUseCredential,
+      [NSURLCredential
+          credentialWithUser:user
+                    password:password
+                 persistence:NSURLCredentialPersistenceForSession]);
+}
+
 #pragma mark - Public methods
 
 - (void)stopLoading {
@@ -1007,10 +1549,6 @@
   [self loadCancelled];
 }
 
-- (base::RepeatingTimer*)safeBrowsingWarningDetectionTimer {
-  return &_safeBrowsingWarningDetectionTimer;
-}
-
 - (void)loadCancelled {
   // TODO(crbug.com/821995):  Check if this function should be removed.
   if (self.navigationState != web::WKNavigationState::FINISHED) {
@@ -1077,6 +1615,8 @@
   return holder;
 }
 
+// Updates current state with any pending information. Should be called when a
+// navigation is committed.
 - (void)commitPendingNavigationInfoInWebView:(WKWebView*)webView {
   if (self.pendingNavigationInfo.referrer) {
     _currentReferrerString = [self.pendingNavigationInfo.referrer copy];
@@ -1143,9 +1683,15 @@
                        web::ReferrerPolicyAlways);
 }
 
-- (void)forgetNullWKNavigation:(WKNavigation*)navigation {
-  if (!navigation)
-    [self.navigationStates removeNavigation:navigation];
+- (void)setLastCommittedNavigationItemTitle:(NSString*)title {
+  DCHECK(title);
+  web::NavigationItem* item =
+      self.navigationManagerImpl->GetLastCommittedItem();
+  if (!item)
+    return;
+
+  item->SetTitle(base::SysNSStringToUTF16(title));
+  self.webStateImpl->OnTitleChanged();
 }
 
 @end
diff --git a/ios/web/public/deprecated/BUILD.gn b/ios/web/public/deprecated/BUILD.gn
index 02df02d..62e57a6 100644
--- a/ios/web/public/deprecated/BUILD.gn
+++ b/ios/web/public/deprecated/BUILD.gn
@@ -32,8 +32,8 @@
 
   deps = [
     ":deprecated",
+    "//ios/web/js_messaging",
     "//ios/web/public",
-    "//ios/web/web_state/ui:web_view_js_utils",
     "//url",
   ]
 
diff --git a/ios/web/public/deprecated/crw_test_js_injection_receiver.mm b/ios/web/public/deprecated/crw_test_js_injection_receiver.mm
index 2d65937a..c755c21 100644
--- a/ios/web/public/deprecated/crw_test_js_injection_receiver.mm
+++ b/ios/web/public/deprecated/crw_test_js_injection_receiver.mm
@@ -7,8 +7,8 @@
 #import <UIKit/UIKit.h>
 #import <WebKit/WebKit.h>
 
+#import "ios/web/js_messaging/web_view_js_utils.h"
 #import "ios/web/public/deprecated/crw_js_injection_evaluator.h"
-#import "ios/web/web_state/ui/web_view_js_utils.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/web/public/test/BUILD.gn b/ios/web/public/test/BUILD.gn
index 58666c50..f4e8d7d 100644
--- a/ios/web/public/test/BUILD.gn
+++ b/ios/web/public/test/BUILD.gn
@@ -15,6 +15,7 @@
     "//base",
     "//base/test:test_support",
     "//ios/web:web",
+    "//ios/web/js_messaging",
     "//ios/web/navigation",
     "//ios/web/navigation:wk_navigation_util",
     "//ios/web/public/deprecated",
@@ -65,10 +66,10 @@
     ":element_selector",
     "//base",
     "//base/test:test_support",
+    "//ios/web/js_messaging",
     "//ios/web/public:public",
     "//ios/web/public/deprecated",
     "//ios/web/web_state:web_state_impl_header",
-    "//ios/web/web_state/js:script_util",
     "//ios/web/web_state/ui:ui",
     "//ios/web/web_view:util",
     "//testing/gtest",
diff --git a/ios/web/public/test/fakes/BUILD.gn b/ios/web/public/test/fakes/BUILD.gn
index 9b17bb5..d7a52cfb 100644
--- a/ios/web/public/test/fakes/BUILD.gn
+++ b/ios/web/public/test/fakes/BUILD.gn
@@ -19,7 +19,6 @@
     "//ios/web/public/session",
     "//ios/web/test:test_constants",
     "//ios/web/web_state/ui:crw_web_view_navigation_proxy",
-    "//ios/web/web_state/ui:web_view_js_utils",
     "//ios/web/webui:webui",
     "//net:test_support",
     "//testing/gtest",
diff --git a/ios/web/public/test/js_test_util.mm b/ios/web/public/test/js_test_util.mm
index ea3b7ceb..9456ccc 100644
--- a/ios/web/public/test/js_test_util.mm
+++ b/ios/web/public/test/js_test_util.mm
@@ -10,9 +10,9 @@
 #include "base/mac/bundle_locations.h"
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
+#import "ios/web/js_messaging/page_script_util.h"
 #import "ios/web/public/deprecated/crw_js_injection_manager.h"
 #import "ios/web/public/deprecated/crw_js_injection_receiver.h"
-#import "ios/web/web_state/js/page_script_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/web/public/test/web_test_with_web_state.mm b/ios/web/public/test/web_test_with_web_state.mm
index 6a480ec3..4b9c330 100644
--- a/ios/web/public/test/web_test_with_web_state.mm
+++ b/ios/web/public/test/web_test_with_web_state.mm
@@ -10,13 +10,13 @@
 #include "base/scoped_observer.h"
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
+#import "ios/web/js_messaging/crw_js_injector.h"
 #import "ios/web/navigation/crw_wk_navigation_states.h"
 #import "ios/web/navigation/navigation_manager_impl.h"
 #import "ios/web/navigation/wk_navigation_util.h"
 #include "ios/web/public/deprecated/url_verification_constants.h"
 #import "ios/web/public/web_client.h"
 #include "ios/web/public/web_state/web_state_observer.h"
-#import "ios/web/web_state/ui/crw_js_injector.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
 #import "ios/web/web_state/web_state_impl.h"
diff --git a/ios/web/public/test/web_view_interaction_test_util.mm b/ios/web/public/test/web_view_interaction_test_util.mm
index 340f0a5..f68af9e2 100644
--- a/ios/web/public/test/web_view_interaction_test_util.mm
+++ b/ios/web/public/test/web_view_interaction_test_util.mm
@@ -10,8 +10,8 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #import "base/test/ios/wait_util.h"
+#import "ios/web/js_messaging/crw_js_injector.h"
 #import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
-#import "ios/web/web_state/ui/crw_js_injector.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 #import "ios/web/web_state/ui/crw_web_view_proxy_impl.h"
 #import "ios/web/web_state/web_state_impl.h"
diff --git a/ios/web/security/BUILD.gn b/ios/web/security/BUILD.gn
index 6505171..b573778 100644
--- a/ios/web/security/BUILD.gn
+++ b/ios/web/security/BUILD.gn
@@ -10,12 +10,12 @@
   deps = [
     "//base",
     "//ios/web/common",
+    "//ios/web/js_messaging",
     "//ios/web/navigation:core",
     "//ios/web/navigation:navigation_manager_util",
     "//ios/web/public",
     "//ios/web/public/security",
     "//ios/web/web_state:web_state_impl_header",
-    "//ios/web/web_state/ui:web_view_js_utils",
     "//net",
     "//ui/gfx/geometry",
   ]
diff --git a/ios/web/security/web_interstitial_impl.h b/ios/web/security/web_interstitial_impl.h
index bd3c523..0900130 100644
--- a/ios/web/security/web_interstitial_impl.h
+++ b/ios/web/security/web_interstitial_impl.h
@@ -8,9 +8,9 @@
 #import <UIKit/UIKit.h>
 
 #import "ios/web/common/crw_content_view.h"
+#import "ios/web/js_messaging/web_view_js_utils.h"
 #include "ios/web/public/security/web_interstitial.h"
 #include "ios/web/public/web_state/web_state_observer.h"
-#import "ios/web/web_state/ui/web_view_js_utils.h"
 #include "url/gurl.h"
 
 @protocol WKNavigationDelegate;
diff --git a/ios/web/web_state/BUILD.gn b/ios/web/web_state/BUILD.gn
index f447090..be14a04 100644
--- a/ios/web/web_state/BUILD.gn
+++ b/ios/web/web_state/BUILD.gn
@@ -10,6 +10,7 @@
     ":web_state_impl_header",
     "//base",
     "//ios/web/common",
+    "//ios/web/js_messaging",
     "//ios/web/navigation",
     "//ios/web/navigation:core",
     "//ios/web/navigation:wk_navigation_util",
@@ -22,6 +23,7 @@
     "//ios/web/session",
     "//ios/web/web_state/ui",
     "//ios/web/web_state/ui:crw_web_view_navigation_proxy",
+    "//ios/web/web_view:util",
     "//ios/web/webui",
     "//net",
     "//ui/gfx",
diff --git a/ios/web/web_state/js/BUILD.gn b/ios/web/web_state/js/BUILD.gn
index 0d9490c..34b0057 100644
--- a/ios/web/web_state/js/BUILD.gn
+++ b/ios/web/web_state/js/BUILD.gn
@@ -6,8 +6,8 @@
 
 source_set("js") {
   deps = [
-    ":script_util",
     "//base",
+    "//ios/web/js_messaging",
     "//ios/web/public",
     "//ios/web/public/deprecated",
   ]
@@ -15,22 +15,6 @@
   sources = [
     "crw_js_injection_manager.mm",
     "crw_js_injection_receiver.mm",
-    "crw_js_window_id_manager.h",
-    "crw_js_window_id_manager.mm",
-  ]
-
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
-source_set("script_util") {
-  deps = [
-    "//base",
-    "//ios/web/public",
-  ]
-
-  sources = [
-    "page_script_util.h",
-    "page_script_util.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/web/web_state/js/crw_js_injection_manager.mm b/ios/web/web_state/js/crw_js_injection_manager.mm
index 5a1a83be..b3701be 100644
--- a/ios/web/web_state/js/crw_js_injection_manager.mm
+++ b/ios/web/web_state/js/crw_js_injection_manager.mm
@@ -9,8 +9,8 @@
 #include "base/logging.h"
 #include "base/mac/bundle_locations.h"
 #include "base/strings/sys_string_conversions.h"
+#import "ios/web/js_messaging/page_script_util.h"
 #import "ios/web/public/deprecated/crw_js_injection_receiver.h"
-#import "ios/web/web_state/js/page_script_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/web/web_state/ui/BUILD.gn b/ios/web/web_state/ui/BUILD.gn
index bb46f6b5..fb5781d6 100644
--- a/ios/web/web_state/ui/BUILD.gn
+++ b/ios/web/web_state/ui/BUILD.gn
@@ -10,7 +10,6 @@
     ":crw_web_view_navigation_proxy",
     ":crw_wk_script_message_router",
     ":favicon_util",
-    ":web_view_js_utils",
     "//base",
     "//ios/net",
     "//ios/web:core",
@@ -48,8 +47,6 @@
   ]
 
   sources = [
-    "crw_js_injector.h",
-    "crw_js_injector.mm",
     "crw_swipe_recognizer_provider.h",
     "crw_touch_tracking_recognizer.h",
     "crw_touch_tracking_recognizer.mm",
@@ -125,9 +122,9 @@
     ":crw_wk_script_message_router",
     "//base",
     "//ios/web/common",
+    "//ios/web/js_messaging",
     "//ios/web/public",
     "//ios/web/web_state/js",
-    "//ios/web/web_state/js:script_util",
     "//ios/web/webui",
   ]
 
@@ -142,22 +139,6 @@
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
-source_set("web_view_js_utils") {
-  deps = [
-    "//base",
-    "//ios/web/public",
-  ]
-
-  sources = [
-    "web_view_js_utils.h",
-    "web_view_js_utils.mm",
-  ]
-
-  libs = [ "WebKit.framework" ]
-
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
 source_set("favicon_util") {
   deps = [
     "//base",
diff --git a/ios/web/web_state/ui/crw_web_controller.h b/ios/web/web_state/ui/crw_web_controller.h
index 53f7f9fd5..e8debc34 100644
--- a/ios/web/web_state/ui/crw_web_controller.h
+++ b/ios/web/web_state/ui/crw_web_controller.h
@@ -129,6 +129,7 @@
 - (BOOL)isViewAlive;
 
 // Returns YES if the current live view is a web view with HTML.
+// TODO(crbug.com/949651): Remove once JSFindInPageManager is removed.
 - (BOOL)contentIsHTML;
 
 // Returns the CRWWebController's view of the current URL. Moreover, this method
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 42b01682..fe1c9b57 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -50,6 +50,7 @@
 #include "ios/web/common/url_util.h"
 #import "ios/web/find_in_page/find_in_page_manager_impl.h"
 #include "ios/web/history_state_util.h"
+#import "ios/web/js_messaging/crw_js_injector.h"
 #import "ios/web/js_messaging/web_frame_impl.h"
 #import "ios/web/js_messaging/web_frames_manager_impl.h"
 #import "ios/web/navigation/crw_navigation_item_holder.h"
@@ -99,7 +100,6 @@
 #import "ios/web/web_state/ui/controller/crw_legacy_native_content_controller.h"
 #import "ios/web/web_state/ui/controller/crw_legacy_native_content_controller_delegate.h"
 #import "ios/web/web_state/ui/crw_context_menu_controller.h"
-#import "ios/web/web_state/ui/crw_js_injector.h"
 #import "ios/web/web_state/ui/crw_swipe_recognizer_provider.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 #import "ios/web/web_state/ui/crw_web_controller_container_view.h"
@@ -114,6 +114,7 @@
 #import "ios/web/web_state/user_interaction_state.h"
 #import "ios/web/web_state/web_state_impl.h"
 #import "ios/web/web_state/web_view_internal_creation_util.h"
+#import "ios/web/web_view/content_type_util.h"
 #import "ios/web/web_view/error_translation_util.h"
 #import "ios/web/web_view/wk_web_view_util.h"
 #import "net/base/mac/url_conversions.h"
@@ -289,8 +290,6 @@
 // Note that this method is expensive, so it should always be cached locally if
 // it's needed multiple times in a method.
 @property(nonatomic, readonly) GURL currentURL;
-// Returns the referrer for the current page.
-@property(nonatomic, readonly) web::Referrer currentReferrer;
 
 // User agent type of the transient item if any, the pending item if a
 // navigation is in progress or the last committed item otherwise.
@@ -419,9 +418,6 @@
 // Sets scroll offset value for webview scroll view from |scrollState|.
 - (void)applyWebViewScrollOffsetFromScrollState:
     (const web::PageScrollState&)scrollState;
-// Sets last committed NavigationItem's title to the given |title|, which can
-// not be nil.
-- (void)setNavigationItemTitle:(NSString*)title;
 // Returns YES if the given WKBackForwardListItem is valid to use for
 // navigation.
 - (BOOL)isBackForwardListItemValid:(WKBackForwardListItem*)item;
@@ -434,27 +430,6 @@
 // Called when a load ends in an SSL error and certificate chain.
 - (void)handleSSLCertError:(NSError*)error
              forNavigation:(WKNavigation*)navigation;
-
-// Used in webView:didReceiveAuthenticationChallenge:completionHandler: to
-// reply with NSURLSessionAuthChallengeDisposition and credentials.
-- (void)processAuthChallenge:(NSURLAuthenticationChallenge*)challenge
-         forCertAcceptPolicy:(web::CertAcceptPolicy)policy
-                  certStatus:(net::CertStatus)certStatus
-           completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition,
-                                       NSURLCredential*))completionHandler;
-// Used in webView:didReceiveAuthenticationChallenge:completionHandler: to reply
-// with NSURLSessionAuthChallengeDisposition and credentials.
-- (void)handleHTTPAuthForChallenge:(NSURLAuthenticationChallenge*)challenge
-                 completionHandler:
-                     (void (^)(NSURLSessionAuthChallengeDisposition,
-                               NSURLCredential*))completionHandler;
-// Used in webView:didReceiveAuthenticationChallenge:completionHandler: to reply
-// with NSURLSessionAuthChallengeDisposition and credentials.
-+ (void)processHTTPAuthForUser:(NSString*)user
-                      password:(NSString*)password
-             completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition,
-                                         NSURLCredential*))completionHandler;
-
 // Loads request for the URL of the current navigation item. Subclasses may
 // choose to build a new NSURLRequest and call |loadRequest| on the underlying
 // web view, or use native web view navigation where possible (for example,
@@ -636,7 +611,7 @@
                         webView:webView];
   }
   [_jsInjector setWebView:webView];
-  [_webView setNavigationDelegate:self];
+  [_webView setNavigationDelegate:self.navigationHandler];
   [_webView setUIDelegate:self.UIHandler];
   for (NSString* keyPath in self.WKWebViewObservers) {
     [_webView addObserver:self forKeyPath:keyPath options:0 context:nullptr];
@@ -857,12 +832,14 @@
 }
 
 - (BOOL)contentIsHTML {
-  if (!self.webView)
-    return NO;
+  return self.webView &&
+         web::IsContentTypeHtml(self.webState->GetContentsMimeType());
+}
 
-  std::string MIMEType = self.webState->GetContentsMimeType();
-  return MIMEType == "text/html" || MIMEType == "application/xhtml+xml" ||
-         MIMEType == "application/xml";
+// Returns YES if the current live view is a web view with an image MIME type.
+- (BOOL)contentIsImage {
+  return self.webView &&
+         web::IsContentTypeImage(self.webState->GetContentsMimeType());
 }
 
 - (GURL)currentURLWithTrustLevel:(web::URLVerificationTrustLevel*)trustLevel {
@@ -1341,17 +1318,6 @@
   }
 }
 
-- (void)setNavigationItemTitle:(NSString*)title {
-  DCHECK(title);
-  web::NavigationItem* item =
-      self.navigationManagerImpl->GetLastCommittedItem();
-  if (!item)
-    return;
-
-  item->SetTitle(base::SysNSStringToUTF16(title));
-  self.webStateImpl->OnTitleChanged();
-}
-
 - (BOOL)isBackForwardListItemValid:(WKBackForwardListItem*)item {
   // The current back-forward list item MUST be in the WKWebView's back-forward
   // list to be valid.
@@ -2287,7 +2253,7 @@
 - (void)legacyNativeContentController:
             (CRWLegacyNativeContentController*)contentController
                 setNativeContentTitle:(NSString*)title {
-  [self setNavigationItemTitle:title];
+  [self.navigationHandler setLastCommittedNavigationItemTitle:title];
 }
 
 - (void)legacyNativeContentController:
@@ -2308,7 +2274,7 @@
 #pragma mark - CRWWebControllerContainerViewDelegate
 
 - (CRWWebViewProxyImpl*)contentViewProxyForContainerView:
-        (CRWWebControllerContainerView*)containerView {
+    (CRWWebControllerContainerView*)containerView {
   return _webViewProxy;
 }
 
@@ -2399,8 +2365,8 @@
         @selector(handleWindowHistoryDidReplaceStateMessage:context:);
     (*handlers)["window.history.forward"] =
         @selector(handleWindowHistoryForwardMessage:context:);
-    (*handlers)["window.history.go"] =
-        @selector(handleWindowHistoryGoMessage:context:);
+    (*handlers)["window.history.go"] = @selector(handleWindowHistoryGoMessage:
+                                                                      context:);
     (*handlers)["restoresession.error"] =
         @selector(handleRestoreSessionErrorMessage:context:);
   });
@@ -2742,7 +2708,7 @@
 
 // Handles 'window.history.didReplaceState' message.
 - (BOOL)handleWindowHistoryDidReplaceStateMessage:
-    (base::DictionaryValue*)message
+            (base::DictionaryValue*)message
                                           context:(NSDictionary*)context {
   if (![context[kIsMainFrame] boolValue])
     return NO;
@@ -2896,83 +2862,6 @@
   self.webStateImpl->ClearWebUI();
 }
 
-#pragma mark - Auth Challenge
-
-- (void)processAuthChallenge:(NSURLAuthenticationChallenge*)challenge
-         forCertAcceptPolicy:(web::CertAcceptPolicy)policy
-                  certStatus:(net::CertStatus)certStatus
-           completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition,
-                                       NSURLCredential*))completionHandler {
-  SecTrustRef trust = challenge.protectionSpace.serverTrust;
-  if (policy == web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER) {
-    // Cert is invalid, but user agreed to proceed, override default behavior.
-    completionHandler(NSURLSessionAuthChallengeUseCredential,
-                      [NSURLCredential credentialForTrust:trust]);
-    return;
-  }
-
-  if (policy != web::CERT_ACCEPT_POLICY_ALLOW &&
-      SecTrustGetCertificateCount(trust)) {
-    // The cert is invalid and the user has not agreed to proceed. Cache the
-    // cert verification result in |_certVerificationErrors|, so that it can
-    // later be reused inside |didFailProvisionalNavigation:|.
-    // The leaf cert is used as the key, because the chain provided by
-    // |didFailProvisionalNavigation:| will differ (it is the server-supplied
-    // chain), thus if intermediates were considered, the keys would mismatch.
-    scoped_refptr<net::X509Certificate> leafCert =
-        net::x509_util::CreateX509CertificateFromSecCertificate(
-            SecTrustGetCertificateAtIndex(trust, 0),
-            std::vector<SecCertificateRef>());
-    if (leafCert) {
-      bool is_recoverable =
-          policy == web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_UNDECIDED_BY_USER;
-      std::string host =
-          base::SysNSStringToUTF8(challenge.protectionSpace.host);
-      _certVerificationErrors->Put(
-          web::CertHostPair(leafCert, host),
-          web::CertVerificationError(is_recoverable, certStatus));
-    }
-  }
-  completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
-}
-
-- (void)handleHTTPAuthForChallenge:(NSURLAuthenticationChallenge*)challenge
-                 completionHandler:
-                     (void (^)(NSURLSessionAuthChallengeDisposition,
-                               NSURLCredential*))completionHandler {
-  NSURLProtectionSpace* space = challenge.protectionSpace;
-  DCHECK(
-      [space.authenticationMethod isEqual:NSURLAuthenticationMethodHTTPBasic] ||
-      [space.authenticationMethod isEqual:NSURLAuthenticationMethodNTLM] ||
-      [space.authenticationMethod isEqual:NSURLAuthenticationMethodHTTPDigest]);
-
-  self.webStateImpl->OnAuthRequired(
-      space, challenge.proposedCredential,
-      base::BindRepeating(^(NSString* user, NSString* password) {
-        [CRWWebController processHTTPAuthForUser:user
-                                        password:password
-                               completionHandler:completionHandler];
-      }));
-}
-
-+ (void)processHTTPAuthForUser:(NSString*)user
-                      password:(NSString*)password
-             completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition,
-                                         NSURLCredential*))completionHandler {
-  DCHECK_EQ(user == nil, password == nil);
-  if (!user || !password) {
-    // Embedder cancelled authentication.
-    completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
-    return;
-  }
-  completionHandler(
-      NSURLSessionAuthChallengeUseCredential,
-      [NSURLCredential
-          credentialWithUser:user
-                    password:password
-                 persistence:NSURLCredentialPersistenceForSession]);
-}
-
 #pragma mark - CRWWebViewScrollViewProxyObserver
 
 - (void)webViewScrollViewDidZoom:
@@ -3462,18 +3351,6 @@
   }
 }
 
-// Called when web view process has been terminated.
-- (void)webViewWebProcessDidCrash {
-  // On iOS 11 WKWebView does not repaint after crash and reload. Recreating
-  // web view fixes the issue. TODO(crbug.com/770914): Remove this workaround
-  // once rdar://35063950 is fixed.
-  [self removeWebView];
-
-  self.navigationHandler.webProcessCrashed = YES;
-  self.webStateImpl->CancelDialogs();
-  self.webStateImpl->OnRenderProcessGone();
-}
-
 // Returns the WKWebViewConfigurationProvider associated with the web
 // controller's BrowserState.
 - (web::WKWebViewConfigurationProvider&)webViewConfigurationProvider {
@@ -3507,484 +3384,6 @@
   return self.webStateImpl;
 }
 
-#pragma mark - WKNavigationDelegate Methods
-
-- (void)webView:(WKWebView*)webView
-    decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction
-                    decisionHandler:
-                        (void (^)(WKNavigationActionPolicy))decisionHandler {
-  [self.navigationHandler webView:webView
-      decidePolicyForNavigationAction:navigationAction
-                      decisionHandler:decisionHandler];
-}
-
-- (void)webView:(WKWebView*)webView
-    decidePolicyForNavigationResponse:(WKNavigationResponse*)WKResponse
-                      decisionHandler:
-                          (void (^)(WKNavigationResponsePolicy))handler {
-  [self.navigationHandler webView:webView
-      decidePolicyForNavigationResponse:WKResponse
-                        decisionHandler:handler];
-}
-
-- (void)webView:(WKWebView*)webView
-    didStartProvisionalNavigation:(WKNavigation*)navigation {
-  [self.navigationHandler webView:webView
-      didStartProvisionalNavigation:navigation];
-}
-
-- (void)webView:(WKWebView*)webView
-    didReceiveServerRedirectForProvisionalNavigation:(WKNavigation*)navigation {
-  [self.navigationHandler webView:webView
-      didReceiveServerRedirectForProvisionalNavigation:navigation];
-}
-
-- (void)webView:(WKWebView*)webView
-    didFailProvisionalNavigation:(WKNavigation*)navigation
-                       withError:(NSError*)error {
-  [self.navigationHandler webView:webView
-      didFailProvisionalNavigation:navigation
-                         withError:error];
-}
-
-- (void)webView:(WKWebView*)webView
-    didCommitNavigation:(WKNavigation*)navigation {
-  [self.navigationHandler webView:webView didCommitNavigation:navigation];
-
-  // For reasons not yet fully understood, sometimes WKWebView triggers
-  // |webView:didFinishNavigation| before |webView:didCommitNavigation|. If a
-  // navigation is already finished, stop processing
-  // (https://crbug.com/818796#c2).
-  if ([self.navigationHandler.navigationStates stateForNavigation:navigation] ==
-      web::WKNavigationState::FINISHED)
-    return;
-
-  BOOL committedNavigation = [self.navigationHandler.navigationStates
-      isCommittedNavigation:navigation];
-  if (!web::features::StorePendingItemInContext()) {
-    // Code in this method relies on existance of pending item.
-    [self.navigationHandler.navigationStates
-             setState:web::WKNavigationState::COMMITTED
-        forNavigation:navigation];
-  }
-
-  DCHECK_EQ(self.webView, webView);
-  _certVerificationErrors->Clear();
-
-  // Invariant: Every |navigation| should have a |context|. Note that violation
-  // of this invariant is currently observed in production, but the cause is not
-  // well understood. This DCHECK is meant to catch such cases in testing if
-  // they arise.
-  // TODO(crbug.com/864769): Remove nullptr checks on |context| in this method
-  // once the root cause of the invariant violation is found.
-  web::NavigationContextImpl* context =
-      [self.navigationHandler.navigationStates contextForNavigation:navigation];
-  DCHECK(context);
-  UMA_HISTOGRAM_BOOLEAN("IOS.CommittedNavigationHasContext", context);
-
-  GURL webViewURL = net::GURLWithNSURL(webView.URL);
-  GURL currentWKItemURL =
-      net::GURLWithNSURL(webView.backForwardList.currentItem.URL);
-  UMA_HISTOGRAM_BOOLEAN("IOS.CommittedURLMatchesCurrentItem",
-                        webViewURL == currentWKItemURL);
-
-  // TODO(crbug.com/787497): Always use webView.backForwardList.currentItem.URL
-  // to obtain lastCommittedURL once loadHTML: is no longer user for WebUI.
-  if (webViewURL.is_empty()) {
-    // It is possible for |webView.URL| to be nil, in which case
-    // webView.backForwardList.currentItem.URL will return the right committed
-    // URL (crbug.com/784480).
-    webViewURL = currentWKItemURL;
-  } else if (context && !context->IsPlaceholderNavigation() &&
-             context->GetUrl() == currentWKItemURL) {
-    // If webView.backForwardList.currentItem.URL matches |context|, then this
-    // is a known edge case where |webView.URL| is wrong.
-    // TODO(crbug.com/826013): Remove this workaround.
-    webViewURL = currentWKItemURL;
-  }
-
-  if (self.navigationHandler.pendingNavigationInfo.MIMEType)
-    context->SetMimeType(self.navigationHandler.pendingNavigationInfo.MIMEType);
-
-  // Don't show webview for placeholder navigation to avoid covering the native
-  // content, which may have already been shown.
-  if (!IsPlaceholderUrl(webViewURL))
-    [self displayWebView];
-
-  // Update HTTP response headers.
-  self.webStateImpl->UpdateHttpResponseHeaders(webViewURL);
-
-  if (@available(iOS 11.3, *)) {
-    // On iOS 11.3 didReceiveServerRedirectForProvisionalNavigation: is not
-    // always called. So if URL was unexpectedly changed then it's probably
-    // because redirect callback was not called.
-    if (@available(iOS 12, *)) {
-      // rdar://37547029 was fixed on iOS 12.
-    } else if (context && !context->IsPlaceholderNavigation() &&
-               context->GetUrl() != webViewURL) {
-      [self.navigationHandler didReceiveRedirectForNavigation:context
-                                                      withURL:webViewURL];
-    }
-  }
-
-  // |context| will be nil if this navigation has been already committed and
-  // finished.
-  if (context) {
-    web::NavigationManager* navigationManager =
-        self.webStateImpl->GetNavigationManager();
-    GURL pendingURL;
-    if (web::features::StorePendingItemInContext() &&
-        navigationManager->GetPendingItemIndex() == -1) {
-      if (context->GetItem()) {
-        pendingURL = context->GetItem()->GetURL();
-      }
-    } else {
-      if (navigationManager->GetPendingItem()) {
-        pendingURL = navigationManager->GetPendingItem()->GetURL();
-      }
-    }
-    if ((pendingURL == webViewURL) || (context->IsLoadingHtmlString()) ||
-        (!web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
-         ui::PageTransitionCoreTypeIs(context->GetPageTransition(),
-                                      ui::PAGE_TRANSITION_RELOAD) &&
-         navigationManager->GetLastCommittedItem())) {
-      // Commit navigation if at least one of these is true:
-      //  - Navigation has pending item (this should always be true, but
-      //    pending item may not exist due to crbug.com/925304).
-      //  - Navigation is loadHTMLString:baseURL: navigation, which does not
-      //    create a pending item, but modifies committed item instead.
-      //  - Transition type is reload with Legacy Navigation Manager (Legacy
-      //    Navigation Manager does not create pending item for reload due to
-      //    crbug.com/676129)
-      context->SetHasCommitted(true);
-    }
-    context->SetResponseHeaders(self.webStateImpl->GetHttpResponseHeaders());
-    self.webStateImpl->SetContentsMimeType(
-        base::SysNSStringToUTF8(context->GetMimeType()));
-  }
-
-  [self.navigationHandler commitPendingNavigationInfoInWebView:webView];
-
-  [self removeAllWebFrames];
-
-  // This point should closely approximate the document object change, so reset
-  // the list of injected scripts to those that are automatically injected.
-  // Do not inject window ID if this is a placeholder URL: window ID is not
-  // needed for native view. For WebUI, let the window ID be injected when the
-  // |loadHTMLString:baseURL| navigation is committed.
-  if (!web::GetWebClient()->IsSlimNavigationManagerEnabled() ||
-      !IsPlaceholderUrl(webViewURL)) {
-    [_jsInjector resetInjectedScriptSet];
-    if ([self contentIsHTML] || [self contentIsImage] ||
-        self.webState->GetContentsMimeType().empty()) {
-      // In unit tests MIME type will be empty, because loadHTML:forURL: does
-      // not notify web view delegate about received response, so web controller
-      // does not get a chance to properly update MIME type.
-      [_jsInjector injectWindowID];
-      web::WebFramesManagerImpl::FromWebState(self.webState)
-          ->RegisterExistingFrames();
-    }
-  }
-
-  if (committedNavigation) {
-    // WKWebView called didCommitNavigation: with incorrect WKNavigation object.
-    // Correct WKNavigation object for this navigation was deallocated because
-    // WKWebView mistakenly cancelled the navigation and called
-    // didFailProvisionalNavigation. As a result web::NavigationContext for this
-    // navigation does not exist anymore. Find correct navigation item and make
-    // it committed.
-    if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
-      bool found_correct_navigation_item = false;
-      for (size_t i = 0; i < self.sessionController.items.size(); i++) {
-        web::NavigationItem* item = self.sessionController.items[i].get();
-        found_correct_navigation_item = item->GetURL() == webViewURL;
-        if (found_correct_navigation_item) {
-          [self.sessionController goToItemAtIndex:i
-                         discardNonCommittedItems:NO];
-          break;
-        }
-      }
-      DCHECK(found_correct_navigation_item);
-    }
-    [self resetDocumentSpecificState];
-    [self didStartLoading];
-  } else if (context) {
-    // If |navigation| is nil (which happens for windows open by DOM), then it
-    // should be the first and the only pending navigation.
-    BOOL isLastNavigation =
-        !navigation ||
-        [[self.navigationHandler.navigationStates lastAddedNavigation]
-            isEqual:navigation];
-    if (isLastNavigation ||
-        (web::features::StorePendingItemInContext() &&
-         self.webState->GetNavigationManager()->GetPendingItemIndex() == -1)) {
-      [self webPageChangedWithContext:context];
-    } else if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
-      // WKWebView has more than one in progress navigation, and committed
-      // navigation was not the latest. Change last committed item to one that
-      // corresponds to committed navigation.
-      int itemIndex = web::GetCommittedItemIndexWithUniqueID(
-          self.navigationManagerImpl, context->GetNavigationItemUniqueID());
-      // Do not discard pending entry, because another pending navigation is
-      // still in progress and will commit or fail soon.
-      [self.sessionController goToItemAtIndex:itemIndex
-                     discardNonCommittedItems:NO];
-    }
-  }
-
-  if (web::features::StorePendingItemInContext()) {
-    // No further code relies an existance of pending item, so this navigation
-    // can be marked as "committed".
-    [self.navigationHandler.navigationStates
-             setState:web::WKNavigationState::COMMITTED
-        forNavigation:navigation];
-  }
-
-  // This is the point where the document's URL has actually changed.
-  [self setDocumentURL:webViewURL context:context];
-
-  if (!committedNavigation && context && !context->IsLoadingErrorPage()) {
-    self.webStateImpl->OnNavigationFinished(context);
-  }
-
-  // Do not update the HTML5 history state or states of the last committed item
-  // for placeholder page because the actual navigation item will not be
-  // committed until the native content or WebUI is shown.
-  if (context && !context->IsPlaceholderNavigation() &&
-      !context->IsLoadingErrorPage() &&
-      !context->GetUrl().SchemeIs(url::kAboutScheme) &&
-      !IsRestoreSessionUrl(context->GetUrl())) {
-    [self updateSSLStatusForCurrentNavigationItem];
-    [self updateHTML5HistoryState];
-    if (!context->IsLoadingErrorPage() && !IsRestoreSessionUrl(webViewURL)) {
-      [self setNavigationItemTitle:self.webView.title];
-    }
-  }
-
-  // Report cases where SSL cert is missing for a secure connection.
-  if (_documentURL.SchemeIsCryptographic()) {
-    scoped_refptr<net::X509Certificate> cert;
-    cert = web::CreateCertFromTrust(self.webView.serverTrust);
-    UMA_HISTOGRAM_BOOLEAN("WebController.WKWebViewHasCertForSecureConnection",
-                          static_cast<bool>(cert));
-  }
-}
-
-- (void)webView:(WKWebView*)webView
-    didFinishNavigation:(WKNavigation*)navigation {
-  [self.navigationHandler webView:webView didFinishNavigation:navigation];
-
-  // Sometimes |webView:didFinishNavigation| arrives before
-  // |webView:didCommitNavigation|. Explicitly trigger post-commit processing.
-  bool navigationCommitted =
-      [self.navigationHandler.navigationStates stateForNavigation:navigation] ==
-      web::WKNavigationState::COMMITTED;
-  UMA_HISTOGRAM_BOOLEAN("IOS.WKWebViewFinishBeforeCommit",
-                        !navigationCommitted);
-  if (!navigationCommitted) {
-    [self webView:webView didCommitNavigation:navigation];
-    DCHECK_EQ(web::WKNavigationState::COMMITTED,
-              [self.navigationHandler.navigationStates
-                  stateForNavigation:navigation]);
-  }
-
-  // Sometimes |didFinishNavigation| callback arrives after |stopLoading| has
-  // been called. Abort in this case.
-  if ([self.navigationHandler.navigationStates stateForNavigation:navigation] ==
-      web::WKNavigationState::NONE) {
-    return;
-  }
-
-  GURL webViewURL = net::GURLWithNSURL(webView.URL);
-  GURL currentWKItemURL =
-      net::GURLWithNSURL(webView.backForwardList.currentItem.URL);
-  UMA_HISTOGRAM_BOOLEAN("IOS.FinishedURLMatchesCurrentItem",
-                        webViewURL == currentWKItemURL);
-
-  web::NavigationContextImpl* context =
-      [self.navigationHandler.navigationStates contextForNavigation:navigation];
-  web::NavigationItemImpl* item =
-      context ? web::GetItemWithUniqueID(self.navigationManagerImpl, context)
-              : nullptr;
-
-  // Invariant: every |navigation| should have a |context| and a |item|.
-  // TODO(crbug.com/899383) Fix invariant violation when a new pending item is
-  // created before a placeholder load finishes.
-  if (IsPlaceholderUrl(webViewURL)) {
-    GURL originalURL = ExtractUrlFromPlaceholderUrl(webViewURL);
-    if (self.currentNavItem != item &&
-        self.currentNavItem->GetVirtualURL() != originalURL) {
-      // The |didFinishNavigation| callback for placeholder navigation can
-      // arrive after another navigation has started. Abort in this case.
-      return;
-    }
-  }
-  DCHECK(context);
-  DCHECK(item);
-  UMA_HISTOGRAM_BOOLEAN("IOS.FinishedNavigationHasContext", context);
-  UMA_HISTOGRAM_BOOLEAN("IOS.FinishedNavigationHasItem", item);
-
-  // TODO(crbug.com/864769): Remove this guard after fixing root cause of
-  // invariant violation in production.
-  if (context && item) {
-    GURL navigationURL = context->IsPlaceholderNavigation()
-                             ? CreatePlaceholderUrlForUrl(context->GetUrl())
-                             : context->GetUrl();
-    if (navigationURL == currentWKItemURL) {
-      // If webView.backForwardList.currentItem.URL matches |context|, then this
-      // is a known edge case where |webView.URL| is wrong.
-      // TODO(crbug.com/826013): Remove this workaround.
-      webViewURL = currentWKItemURL;
-    }
-
-    if (!IsWKInternalUrl(currentWKItemURL) && currentWKItemURL == webViewURL &&
-        currentWKItemURL != context->GetUrl() &&
-        item == self.navigationManagerImpl->GetLastCommittedItem() &&
-        item->GetURL().GetOrigin() == currentWKItemURL.GetOrigin()) {
-      // WKWebView sometimes changes URL on the same navigation, likely due to
-      // location.replace() or history.replaceState in onload handler that does
-      // not change the origin. It's safe to update |item| and |context| URL
-      // because they are both associated to WKNavigation*, which is a stable ID
-      // for the navigation. See https://crbug.com/869540 for a real-world case.
-      item->SetURL(currentWKItemURL);
-      context->SetUrl(currentWKItemURL);
-    }
-
-    if (IsPlaceholderUrl(webViewURL)) {
-      if (item->GetURL() == webViewURL) {
-        // Current navigation item is restored from a placeholder URL as part
-        // of session restoration. It is now safe to update the navigation
-        // item URL to the original app-specific URL.
-        item->SetURL(ExtractUrlFromPlaceholderUrl(webViewURL));
-      }
-
-      if ([self.legacyNativeController
-              shouldLoadURLInNativeView:item->GetURL()]) {
-        [self.legacyNativeController
-            webViewDidFinishNavigationWithContext:context
-                                          andItem:item];
-      } else if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
-                 item->error_retry_state_machine().state() ==
-                     web::ErrorRetryState::kNoNavigationError) {
-        // Offline pages can leave the WKBackForwardList current item as a
-        // placeholder with no saved content.  In this case, trigger a retry
-        // on that navigation with an update |item| url and |context| error.
-        item->SetURL(
-            ExtractUrlFromPlaceholderUrl(net::GURLWithNSURL(self.webView.URL)));
-        item->SetVirtualURL(item->GetURL());
-        context->SetError([NSError
-            errorWithDomain:NSURLErrorDomain
-                       code:NSURLErrorNetworkConnectionLost
-                   userInfo:@{
-                     NSURLErrorFailingURLStringErrorKey :
-                         base::SysUTF8ToNSString(item->GetURL().spec())
-                   }]);
-        item->error_retry_state_machine().SetRetryPlaceholderNavigation();
-      }
-    }
-
-    web::ErrorRetryCommand command =
-        item->error_retry_state_machine().DidFinishNavigation(webViewURL);
-    [self handleErrorRetryCommand:command
-                   navigationItem:item
-                navigationContext:context
-               originalNavigation:navigation];
-  }
-
-  [self.navigationHandler.navigationStates
-           setState:web::WKNavigationState::FINISHED
-      forNavigation:navigation];
-
-  DCHECK(!_isHalted);
-  // Trigger JavaScript driven post-document-load-completion tasks.
-  // TODO(crbug.com/546350): Investigate using
-  // WKUserScriptInjectionTimeAtDocumentEnd to inject this material at the
-  // appropriate time rather than invoking here.
-  web::ExecuteJavaScript(webView, @"__gCrWeb.didFinishNavigation()", nil);
-  [self didFinishNavigation:context];
-
-  if (web::features::StorePendingItemInContext()) {
-    // Remove the navigation to immediately get rid of pending item.
-    if (web::WKNavigationState::NONE != [self.navigationHandler.navigationStates
-                                            stateForNavigation:navigation]) {
-      [self.navigationHandler.navigationStates removeNavigation:navigation];
-    }
-  } else {
-    [self.navigationHandler forgetNullWKNavigation:navigation];
-  }
-}
-
-- (void)webView:(WKWebView*)webView
-    didFailNavigation:(WKNavigation*)navigation
-            withError:(NSError*)error {
-  [self.navigationHandler webView:webView
-                didFailNavigation:navigation
-                        withError:error];
-
-  [self.navigationHandler.navigationStates
-           setState:web::WKNavigationState::FAILED
-      forNavigation:navigation];
-
-  [self handleLoadError:error forNavigation:navigation provisionalLoad:NO];
-
-  [self removeAllWebFrames];
-  _certVerificationErrors->Clear();
-  [self.navigationHandler forgetNullWKNavigation:navigation];
-}
-
-- (void)webView:(WKWebView*)webView
-    didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
-                    completionHandler:
-                        (void (^)(NSURLSessionAuthChallengeDisposition,
-                                  NSURLCredential*))completionHandler {
-  [self.navigationHandler webView:webView
-      didReceiveAuthenticationChallenge:challenge
-                      completionHandler:completionHandler];
-
-  NSString* authMethod = challenge.protectionSpace.authenticationMethod;
-  if ([authMethod isEqual:NSURLAuthenticationMethodHTTPBasic] ||
-      [authMethod isEqual:NSURLAuthenticationMethodNTLM] ||
-      [authMethod isEqual:NSURLAuthenticationMethodHTTPDigest]) {
-    [self handleHTTPAuthForChallenge:challenge
-                   completionHandler:completionHandler];
-    return;
-  }
-
-  if (![authMethod isEqual:NSURLAuthenticationMethodServerTrust]) {
-    completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
-    return;
-  }
-
-  SecTrustRef trust = challenge.protectionSpace.serverTrust;
-  base::ScopedCFTypeRef<SecTrustRef> scopedTrust(trust,
-                                                 base::scoped_policy::RETAIN);
-  __weak CRWWebController* weakSelf = self;
-  [_certVerificationController
-      decideLoadPolicyForTrust:scopedTrust
-                          host:challenge.protectionSpace.host
-             completionHandler:^(web::CertAcceptPolicy policy,
-                                 net::CertStatus status) {
-               CRWWebController* strongSelf = weakSelf;
-               if (!strongSelf) {
-                 completionHandler(
-                     NSURLSessionAuthChallengeRejectProtectionSpace, nil);
-                 return;
-               }
-               [strongSelf processAuthChallenge:challenge
-                            forCertAcceptPolicy:policy
-                                     certStatus:status
-                              completionHandler:completionHandler];
-             }];
-}
-
-- (void)webViewWebContentProcessDidTerminate:(WKWebView*)webView {
-  [self.navigationHandler webViewWebContentProcessDidTerminate:webView];
-
-  _certVerificationErrors->Clear();
-  [self removeAllWebFrames];
-  [self webViewWebProcessDidCrash];
-}
-
 #pragma mark - WKNavigationDelegate Helpers
 
 // Called when the web page has changed document and/or URL, and so the page
@@ -4232,16 +3631,6 @@
   return provisionalLoad;
 }
 
-// Returns YES if the current live view is a web view with an image MIME type.
-- (BOOL)contentIsImage {
-  if (!self.webView)
-    return NO;
-
-  const std::string image = "image";
-  std::string MIMEType = self.webState->GetContentsMimeType();
-  return MIMEType.compare(0, image.length(), image) == 0;
-}
-
 #pragma mark - CRWSSLStatusUpdaterDataSource
 
 - (void)SSLStatusUpdater:(CRWSSLStatusUpdater*)SSLStatusUpdater
@@ -4449,7 +3838,8 @@
       !IsPlaceholderUrl(net::GURLWithNSURL(self.webView.URL))) {
     // Do not update the title if there is a navigation in progress because
     // there is no way to tell if KVO change fired for new or previous page.
-    [self setNavigationItemTitle:self.webView.title];
+    [self.navigationHandler
+        setLastCommittedNavigationItemTitle:self.webView.title];
   }
 }
 
@@ -4757,12 +4147,29 @@
   return &_userInteractionState;
 }
 
+- (CRWJSInjector*)JSInjectorForNavigationHandler:
+    (CRWWKNavigationHandler*)navigationHandler {
+  return self.jsInjector;
+}
+
+- (CRWLegacyNativeContentController*)
+    legacyNativeContentControllerForNavigationHandler:
+        (CRWWKNavigationHandler*)navigationHandler {
+  return self.legacyNativeController;
+}
+
 - (web::CertVerificationErrorsCacheType*)
     certVerificationErrorsForNavigationHandler:
         (CRWWKNavigationHandler*)navigationHandler {
   return _certVerificationErrors.get();
 }
 
+- (CRWCertVerificationController*)
+    certVerificationControllerForNavigationHandler:
+        (CRWWKNavigationHandler*)navigationHandler {
+  return _certVerificationController;
+}
+
 - (GURL)navigationHandlerDocumentURL:
     (CRWWKNavigationHandler*)navigationHandler {
   return _documentURL;
@@ -4847,6 +4254,64 @@
   [self removeAllWebFrames];
 }
 
+- (void)navigationHandlerDisplayWebView:
+    (CRWWKNavigationHandler*)navigationHandler {
+  [self displayWebView];
+}
+
+- (void)navigationHandlerResetDocumentSpecificState:
+    (CRWWKNavigationHandler*)navigationHandler {
+  [self resetDocumentSpecificState];
+}
+
+- (void)navigationHandlerDidStartLoading:
+    (CRWWKNavigationHandler*)navigationHandler {
+  [self didStartLoading];
+}
+
+- (void)navigationHandler:(CRWWKNavigationHandler*)navigationHandler
+    didChangePageWithContext:(web::NavigationContextImpl*)context {
+  [self webPageChangedWithContext:context];
+}
+
+- (void)navigationHandlerUpdateSSLStatusForCurrentNavigationItem:
+    (CRWWKNavigationHandler*)navigationHandler {
+  [self updateSSLStatusForCurrentNavigationItem];
+}
+
+- (void)navigationHandlerUpdateHTML5HistoryState:
+    (CRWWKNavigationHandler*)navigationHandler {
+  [self updateHTML5HistoryState];
+}
+
+- (void)navigationHandler:(CRWWKNavigationHandler*)navigationHandler
+    handleErrorRetryCommand:(web::ErrorRetryCommand)command
+             navigationItem:(web::NavigationItemImpl*)item
+          navigationContext:(web::NavigationContextImpl*)context
+         originalNavigation:(WKNavigation*)originalNavigation {
+  [self handleErrorRetryCommand:command
+                 navigationItem:item
+              navigationContext:context
+             originalNavigation:originalNavigation];
+}
+
+- (void)navigationHandler:(CRWWKNavigationHandler*)navigationHandler
+      didFinishNavigation:(web::NavigationContextImpl*)context {
+  [self didFinishNavigation:context];
+}
+
+- (void)navigationHandlerWebProcessDidCrash:
+    (CRWWKNavigationHandler*)navigationHandler {
+  [self removeAllWebFrames];
+  // On iOS 11 WKWebView does not repaint after crash and reload. Recreating
+  // web view fixes the issue. TODO(crbug.com/770914): Remove this workaround
+  // once rdar://35063950 is fixed.
+  [self removeWebView];
+
+  self.webStateImpl->CancelDialogs();
+  self.webStateImpl->OnRenderProcessGone();
+}
+
 #pragma mark - Testing-Only Methods
 
 - (void)injectWebViewContentView:(CRWWebViewContentView*)webViewContentView {
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index f73d668..e5393593 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -25,6 +25,8 @@
 #import "ios/web/navigation/navigation_manager_impl.h"
 #import "ios/web/navigation/wk_navigation_action_policy_util.h"
 
+#import "ios/web/js_messaging/crw_js_injector.h"
+#import "ios/web/js_messaging/web_view_js_utils.h"
 #import "ios/web/public/deprecated/crw_native_content.h"
 #import "ios/web/public/deprecated/crw_native_content_holder.h"
 #import "ios/web/public/deprecated/crw_native_content_provider.h"
@@ -53,10 +55,8 @@
 #include "ios/web/test/test_url_constants.h"
 #import "ios/web/test/web_test_with_web_controller.h"
 #import "ios/web/test/wk_web_view_crash_utils.h"
-#import "ios/web/web_state/ui/crw_js_injector.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 #import "ios/web/web_state/ui/crw_web_controller_container_view.h"
-#import "ios/web/web_state/ui/web_view_js_utils.h"
 #import "ios/web/web_state/web_state_impl.h"
 #import "net/base/mac/url_conversions.h"
 #include "net/cert/x509_util_ios_and_mac.h"
diff --git a/ios/web/web_state/ui/web_view_js_utils_unittest.mm b/ios/web/web_state/ui/web_view_js_utils_unittest.mm
index 857fc2e..bdaf60b7 100644
--- a/ios/web/web_state/ui/web_view_js_utils_unittest.mm
+++ b/ios/web/web_state/ui/web_view_js_utils_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/ui/web_view_js_utils.h"
+#import "ios/web/js_messaging/web_view_js_utils.h"
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
diff --git a/ios/web/web_state/ui/wk_web_view_configuration_provider.mm b/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
index 21fe8b2..e60a18f 100644
--- a/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
+++ b/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
@@ -12,9 +12,9 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "ios/web/common/features.h"
+#import "ios/web/js_messaging/page_script_util.h"
 #include "ios/web/public/browser_state.h"
 #include "ios/web/public/web_client.h"
-#import "ios/web/web_state/js/page_script_util.h"
 #import "ios/web/web_state/ui/crw_wk_script_message_router.h"
 #import "ios/web/web_state/ui/wk_web_view_configuration_provider_observer.h"
 #import "ios/web/webui/crw_web_ui_scheme_handler.h"
diff --git a/ios/web/web_state/ui/wk_web_view_configuration_provider_unittest.mm b/ios/web/web_state/ui/wk_web_view_configuration_provider_unittest.mm
index aad7071..6d2da2ef 100644
--- a/ios/web/web_state/ui/wk_web_view_configuration_provider_unittest.mm
+++ b/ios/web/web_state/ui/wk_web_view_configuration_provider_unittest.mm
@@ -7,11 +7,11 @@
 #import <WebKit/WebKit.h>
 
 #include "base/memory/ptr_util.h"
+#import "ios/web/js_messaging/page_script_util.h"
 #include "ios/web/public/test/fakes/test_browser_state.h"
 #include "ios/web/public/test/scoped_testing_web_client.h"
 #import "ios/web/public/web_client.h"
 #import "ios/web/test/fakes/fake_wk_configuration_provider_observer.h"
-#import "ios/web/web_state/js/page_script_util.h"
 #import "ios/web/web_state/ui/crw_wk_script_message_router.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index e90a491..ddfb7699 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -15,6 +15,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #import "ios/web/common/crw_content_view.h"
 #include "ios/web/common/url_util.h"
+#import "ios/web/js_messaging/crw_js_injector.h"
 #import "ios/web/navigation/crw_session_controller.h"
 #import "ios/web/navigation/legacy_navigation_manager_impl.h"
 #import "ios/web/navigation/navigation_context_impl.h"
@@ -42,7 +43,6 @@
 #import "ios/web/security/web_interstitial_impl.h"
 #import "ios/web/session/session_certificate_policy_cache_impl.h"
 #include "ios/web/web_state/global_web_state_event_tracker.h"
-#import "ios/web/web_state/ui/crw_js_injector.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 #import "ios/web/web_state/ui/crw_web_controller_container_view.h"
 #import "ios/web/web_state/ui/crw_web_view_navigation_proxy.h"
diff --git a/ios/web/web_view/BUILD.gn b/ios/web/web_view/BUILD.gn
index e59f0a7c2..b1c42cb 100644
--- a/ios/web/web_view/BUILD.gn
+++ b/ios/web/web_view/BUILD.gn
@@ -6,6 +6,8 @@
 
 source_set("util") {
   sources = [
+    "content_type_util.cc",
+    "content_type_util.h",
     "error_translation_util.h",
     "error_translation_util.mm",
     "wk_web_view_util.h",
@@ -38,6 +40,7 @@
   ]
 
   sources = [
+    "content_type_util_unittest.cc",
     "error_translation_util_unittest.mm",
     "wk_web_view_util_unittest.mm",
   ]
diff --git a/ios/web/web_view/content_type_util.cc b/ios/web/web_view/content_type_util.cc
new file mode 100644
index 0000000..de72913
--- /dev/null
+++ b/ios/web/web_view/content_type_util.cc
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/web/web_view/content_type_util.h"
+#include "base/stl_util.h"
+
+namespace web {
+
+bool IsContentTypeHtml(const std::string& mime_type) {
+  return mime_type == "text/html" || mime_type == "application/xhtml+xml" ||
+         mime_type == "application/xml";
+}
+
+bool IsContentTypeImage(const std::string& mime_type) {
+  const std::string image = "image";
+  return mime_type.compare(0, image.size(), image) == 0;
+}
+
+}  // namespace web
diff --git a/ios/web/web_view/content_type_util.h b/ios/web/web_view/content_type_util.h
new file mode 100644
index 0000000..3b369c81
--- /dev/null
+++ b/ios/web/web_view/content_type_util.h
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_WEB_VIEW_CONTENT_TYPE_UTIL_H_
+#define IOS_WEB_WEB_VIEW_CONTENT_TYPE_UTIL_H_
+
+#include <string>
+
+namespace web {
+
+// Returns true if |mime_type| is one of:
+//   1. text/html;
+//   2. application/xhtml+xml;
+//   3. application/xml.
+bool IsContentTypeHtml(const std::string& mime_type);
+// Returns true if |mime_type| begins with "image".
+bool IsContentTypeImage(const std::string& mime_type);
+
+}  // namespace web
+
+#endif  // IOS_WEB_WEB_VIEW_CONTENT_TYPE_UTIL_H_
diff --git a/ios/web/web_view/content_type_util_unittest.cc b/ios/web/web_view/content_type_util_unittest.cc
new file mode 100644
index 0000000..ac788cbe
--- /dev/null
+++ b/ios/web/web_view/content_type_util_unittest.cc
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/web/web_view/content_type_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace web {
+
+class ContentTypeUtilTest : public PlatformTest {};
+
+TEST_F(ContentTypeUtilTest, TestIsContentTypeHtml) {
+  EXPECT_TRUE(IsContentTypeHtml("text/html"));
+  EXPECT_TRUE(IsContentTypeHtml("application/xhtml+xml"));
+  EXPECT_TRUE(IsContentTypeHtml("application/xml"));
+
+  EXPECT_FALSE(IsContentTypeHtml("text/xhtml"));
+  EXPECT_FALSE(IsContentTypeHtml("application"));
+  EXPECT_FALSE(IsContentTypeHtml("application/html"));
+  EXPECT_FALSE(IsContentTypeHtml("application/xhtml"));
+}
+
+TEST_F(ContentTypeUtilTest, TestIsContentTypeImage) {
+  EXPECT_TRUE(IsContentTypeImage("image/bmp"));
+  EXPECT_TRUE(IsContentTypeImage("image/gif"));
+  EXPECT_TRUE(IsContentTypeImage("image/png"));
+  EXPECT_TRUE(IsContentTypeImage("image/x-icon"));
+
+  EXPECT_FALSE(IsContentTypeImage("imag"));
+  EXPECT_FALSE(IsContentTypeImage("text/html"));
+}
+
+}  // namespace web
diff --git a/ios/web_view/internal/signin/web_view_identity_manager_factory.mm b/ios/web_view/internal/signin/web_view_identity_manager_factory.mm
index e0dd681..b814bb9 100644
--- a/ios/web_view/internal/signin/web_view_identity_manager_factory.mm
+++ b/ios/web_view/internal/signin/web_view_identity_manager_factory.mm
@@ -147,7 +147,8 @@
 
   auto primary_account_mutator =
       std::make_unique<identity::PrimaryAccountMutatorImpl>(
-          account_tracker_service.get(), signin_manager.get());
+          account_tracker_service.get(), signin_manager.get(),
+          browser_state->GetPrefs());
 
   auto accounts_cookie_mutator =
       std::make_unique<identity::AccountsCookieMutatorImpl>(
diff --git a/media/base/media_track.cc b/media/base/media_track.cc
index bbffffa5..a8fb2727 100644
--- a/media/base/media_track.cc
+++ b/media/base/media_track.cc
@@ -8,9 +8,9 @@
 
 MediaTrack::MediaTrack(Type type,
                        StreamParser::TrackId bytestream_track_id,
-                       const std::string& kind,
-                       const std::string& label,
-                       const std::string& lang)
+                       const Kind& kind,
+                       const Label& label,
+                       const Language& lang)
     : type_(type),
       bytestream_track_id_(bytestream_track_id),
       kind_(kind),
diff --git a/media/base/media_track.h b/media/base/media_track.h
index 413c8e0..a4cbb7a 100644
--- a/media/base/media_track.h
+++ b/media/base/media_track.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/util/type_safety/strong_alias.h"
 #include "media/base/media_export.h"
 #include "media/base/stream_parser.h"
 
@@ -15,12 +16,15 @@
 class MEDIA_EXPORT MediaTrack {
  public:
   enum Type { Text, Audio, Video };
-  using Id = std::string;
+  using Id = util::StrongAlias<class IdTag, std::string>;
+  using Kind = util::StrongAlias<class KindTag, std::string>;
+  using Label = util::StrongAlias<class LabelTag, std::string>;
+  using Language = util::StrongAlias<class LanguageTag, std::string>;
   MediaTrack(Type type,
              StreamParser::TrackId bytestream_track_id,
-             const std::string& kind,
-             const std::string& label,
-             const std::string& lang);
+             const Kind& kind,
+             const Label& label,
+             const Language& lang);
   ~MediaTrack();
 
   Type type() const { return type_; }
@@ -28,14 +32,14 @@
   StreamParser::TrackId bytestream_track_id() const {
     return bytestream_track_id_;
   }
-  const std::string& kind() const { return kind_; }
-  const std::string& label() const { return label_; }
-  const std::string& language() const { return language_; }
+  const Kind& kind() const { return kind_; }
+  const Label& label() const { return label_; }
+  const Language& language() const { return language_; }
 
   Id id() const { return id_; }
   void set_id(Id id) {
-    DCHECK(id_.empty());
-    DCHECK(!id.empty());
+    DCHECK(id_.value().empty());
+    DCHECK(!id.value().empty());
     id_ = id;
   }
 
@@ -55,9 +59,9 @@
 
   // These properties are read from input streams by stream parsers as specified
   // in https://dev.w3.org/html5/html-sourcing-inband-tracks/.
-  std::string kind_;
-  std::string label_;
-  std::string language_;
+  Kind kind_;
+  Label label_;
+  Language language_;
 };
 
 // Helper for logging.
diff --git a/media/base/media_tracks.cc b/media/base/media_tracks.cc
index 13bfa102..39da608 100644
--- a/media/base/media_tracks.cc
+++ b/media/base/media_tracks.cc
@@ -20,9 +20,9 @@
 MediaTrack* MediaTracks::AddAudioTrack(
     const AudioDecoderConfig& config,
     StreamParser::TrackId bytestream_track_id,
-    const std::string& kind,
-    const std::string& label,
-    const std::string& language) {
+    const MediaTrack::Kind& kind,
+    const MediaTrack::Label& label,
+    const MediaTrack::Language& language) {
   DCHECK(config.IsValidConfig());
   CHECK(audio_configs_.find(bytestream_track_id) == audio_configs_.end());
   std::unique_ptr<MediaTrack> track = std::make_unique<MediaTrack>(
@@ -36,9 +36,9 @@
 MediaTrack* MediaTracks::AddVideoTrack(
     const VideoDecoderConfig& config,
     StreamParser::TrackId bytestream_track_id,
-    const std::string& kind,
-    const std::string& label,
-    const std::string& language) {
+    const MediaTrack::Kind& kind,
+    const MediaTrack::Label& label,
+    const MediaTrack::Language& language) {
   DCHECK(config.IsValidConfig());
   CHECK(video_configs_.find(bytestream_track_id) == video_configs_.end());
   std::unique_ptr<MediaTrack> track = std::make_unique<MediaTrack>(
diff --git a/media/base/media_tracks.h b/media/base/media_tracks.h
index c7bfa8c..900c3b1 100644
--- a/media/base/media_tracks.h
+++ b/media/base/media_tracks.h
@@ -30,16 +30,16 @@
   // track within the bytestream.
   MediaTrack* AddAudioTrack(const AudioDecoderConfig& config,
                             StreamParser::TrackId bytestream_track_id,
-                            const std::string& kind,
-                            const std::string& label,
-                            const std::string& language);
+                            const MediaTrack::Kind& kind,
+                            const MediaTrack::Label& label,
+                            const MediaTrack::Language& language);
   // Adds a new video track. The |bytestreamTrackId| must uniquely identify the
   // track within the bytestream.
   MediaTrack* AddVideoTrack(const VideoDecoderConfig& config,
                             StreamParser::TrackId bytestream_track_id,
-                            const std::string& kind,
-                            const std::string& label,
-                            const std::string& language);
+                            const MediaTrack::Kind& kind,
+                            const MediaTrack::Label& label,
+                            const MediaTrack::Language& language);
 
   const MediaTracksCollection& tracks() const { return tracks_; }
 
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 77f7ec4..0e80be5 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -967,7 +967,7 @@
   std::ostringstream logstr;
   std::vector<MediaTrack::Id> enabledMediaTrackIds;
   for (const auto& blinkTrackId : enabledTrackIds) {
-    MediaTrack::Id track_id = blinkTrackId.Utf8().data();
+    const auto track_id = MediaTrack::Id(blinkTrackId.Utf8().data());
     logstr << track_id << " ";
     enabledMediaTrackIds.push_back(track_id);
   }
@@ -984,8 +984,8 @@
   if (selectedTrackId && !video_track_disabled_)
     selected_video_track_id = MediaTrack::Id(selectedTrackId->Utf8().data());
   MEDIA_LOG(INFO, media_log_.get())
-      << "Selected video track: [" << selected_video_track_id.value_or("")
-      << "]";
+      << "Selected video track: ["
+      << selected_video_track_id.value_or(MediaTrack::Id()) << "]";
   pipeline_controller_->OnSelectedVideoTrackChanged(selected_video_track_id);
 }
 
@@ -1418,18 +1418,20 @@
   bool is_first_video_track = true;
   for (const auto& track : tracks->tracks()) {
     if (track->type() == MediaTrack::Audio) {
-      client_->AddAudioTrack(blink::WebString::FromUTF8(track->id()),
-                             blink::WebMediaPlayerClient::kAudioTrackKindMain,
-                             blink::WebString::FromUTF8(track->label()),
-                             blink::WebString::FromUTF8(track->language()),
-                             is_first_audio_track);
+      client_->AddAudioTrack(
+          blink::WebString::FromUTF8(track->id().value()),
+          blink::WebMediaPlayerClient::kAudioTrackKindMain,
+          blink::WebString::FromUTF8(track->label().value()),
+          blink::WebString::FromUTF8(track->language().value()),
+          is_first_audio_track);
       is_first_audio_track = false;
     } else if (track->type() == MediaTrack::Video) {
-      client_->AddVideoTrack(blink::WebString::FromUTF8(track->id()),
-                             blink::WebMediaPlayerClient::kVideoTrackKindMain,
-                             blink::WebString::FromUTF8(track->label()),
-                             blink::WebString::FromUTF8(track->language()),
-                             is_first_video_track);
+      client_->AddVideoTrack(
+          blink::WebString::FromUTF8(track->id().value()),
+          blink::WebMediaPlayerClient::kVideoTrackKindMain,
+          blink::WebString::FromUTF8(track->label().value()),
+          blink::WebString::FromUTF8(track->language().value()),
+          is_first_video_track);
       is_first_video_track = false;
     } else {
       // Text tracks are not supported through this code path yet.
diff --git a/media/blink/websourcebuffer_impl.cc b/media/blink/websourcebuffer_impl.cc
index dc5c56ce..d1a4240f 100644
--- a/media/blink/websourcebuffer_impl.cc
+++ b/media/blink/websourcebuffer_impl.cc
@@ -224,12 +224,12 @@
   for (const auto& track : tracks->tracks()) {
     blink::WebSourceBufferClient::MediaTrackInfo trackInfo;
     trackInfo.track_type = mediaTrackTypeToBlink(track->type());
-    trackInfo.id = blink::WebString::FromUTF8(track->id());
+    trackInfo.id = blink::WebString::FromUTF8(track->id().value());
     trackInfo.byte_stream_track_id = blink::WebString::FromUTF8(
         base::NumberToString(track->bytestream_track_id()));
-    trackInfo.kind = blink::WebString::FromUTF8(track->kind());
-    trackInfo.label = blink::WebString::FromUTF8(track->label());
-    trackInfo.language = blink::WebString::FromUTF8(track->language());
+    trackInfo.kind = blink::WebString::FromUTF8(track->kind().value());
+    trackInfo.label = blink::WebString::FromUTF8(track->label().value());
+    trackInfo.language = blink::WebString::FromUTF8(track->language().value());
     trackInfoVector.push_back(trackInfo);
   }
 
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 6c20ba2..18255d9 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -1301,7 +1301,7 @@
 // static
 MediaTrack::Id ChunkDemuxer::GenerateMediaTrackId() {
   static unsigned g_track_count = 0;
-  return base::NumberToString(++g_track_count);
+  return MediaTrack::Id(base::NumberToString(++g_track_count));
 }
 
 ChunkDemuxerStream* ChunkDemuxer::CreateDemuxerStream(
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index b8f209af..bb001ab 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -1400,12 +1400,14 @@
 
     StreamParser::TrackId track_id =
         static_cast<StreamParser::TrackId>(media_tracks->tracks().size() + 1);
-    std::string track_label = streams_[i]->GetMetadata("handler_name");
-    std::string track_language = streams_[i]->GetMetadata("language");
+    auto track_label =
+        MediaTrack::Label(streams_[i]->GetMetadata("handler_name"));
+    auto track_language =
+        MediaTrack::Language(streams_[i]->GetMetadata("language"));
 
     // Some metadata is named differently in FFmpeg for webm files.
     if (glue_->container() == container_names::CONTAINER_WEBM)
-      track_label = streams_[i]->GetMetadata("title");
+      track_label = MediaTrack::Label(streams_[i]->GetMetadata("title"));
 
     if (codec_type == AVMEDIA_TYPE_AUDIO) {
       ++supported_audio_track_count;
@@ -1437,9 +1439,10 @@
       AudioDecoderConfig audio_config = streams_[i]->audio_decoder_config();
       RecordAudioCodecStats(audio_config);
 
-      media_track = media_tracks->AddAudioTrack(audio_config, track_id, "main",
+      media_track = media_tracks->AddAudioTrack(audio_config, track_id,
+                                                MediaTrack::Kind("main"),
                                                 track_label, track_language);
-      media_track->set_id(base::NumberToString(track_id));
+      media_track->set_id(MediaTrack::Id(base::NumberToString(track_id)));
       DCHECK(track_id_to_demux_stream_map_.find(media_track->id()) ==
              track_id_to_demux_stream_map_.end());
       track_id_to_demux_stream_map_[media_track->id()] = streams_[i].get();
@@ -1449,9 +1452,10 @@
       RecordVideoCodecStats(glue_->container(), video_config,
                             stream->codecpar->color_range, media_log_);
 
-      media_track = media_tracks->AddVideoTrack(video_config, track_id, "main",
+      media_track = media_tracks->AddVideoTrack(video_config, track_id,
+                                                MediaTrack::Kind("main"),
                                                 track_label, track_language);
-      media_track->set_id(base::NumberToString(track_id));
+      media_track->set_id(MediaTrack::Id(base::NumberToString(track_id)));
       DCHECK(track_id_to_demux_stream_map_.find(media_track->id()) ==
              track_id_to_demux_stream_map_.end());
       track_id_to_demux_stream_map_[media_track->id()] = streams_[i].get();
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index db87c5f..198e4d9 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -1449,16 +1449,16 @@
   const MediaTrack& audio_track = *(media_tracks_->tracks()[1]);
   EXPECT_EQ(audio_track.type(), MediaTrack::Audio);
   EXPECT_EQ(audio_track.bytestream_track_id(), 2);
-  EXPECT_EQ(audio_track.kind(), "main");
-  EXPECT_EQ(audio_track.label(), "SoundHandler");
-  EXPECT_EQ(audio_track.language(), "und");
+  EXPECT_EQ(audio_track.kind().value(), "main");
+  EXPECT_EQ(audio_track.label().value(), "SoundHandler");
+  EXPECT_EQ(audio_track.language().value(), "und");
 
   const MediaTrack& video_track = *(media_tracks_->tracks()[0]);
   EXPECT_EQ(video_track.type(), MediaTrack::Video);
   EXPECT_EQ(video_track.bytestream_track_id(), 1);
-  EXPECT_EQ(video_track.kind(), "main");
-  EXPECT_EQ(video_track.label(), "VideoHandler");
-  EXPECT_EQ(video_track.language(), "und");
+  EXPECT_EQ(video_track.kind().value(), "main");
+  EXPECT_EQ(video_track.label().value(), "VideoHandler");
+  EXPECT_EQ(video_track.language().value(), "und");
 }
 
 TEST_F(FFmpegDemuxerTest, Read_Mp4_Multiple_Tracks) {
@@ -1470,30 +1470,30 @@
   const MediaTrack& video_track = *(media_tracks_->tracks()[0]);
   EXPECT_EQ(video_track.type(), MediaTrack::Video);
   EXPECT_EQ(video_track.bytestream_track_id(), 1);
-  EXPECT_EQ(video_track.kind(), "main");
-  EXPECT_EQ(video_track.label(), "VideoHandler");
-  EXPECT_EQ(video_track.language(), "und");
+  EXPECT_EQ(video_track.kind().value(), "main");
+  EXPECT_EQ(video_track.label().value(), "VideoHandler");
+  EXPECT_EQ(video_track.language().value(), "und");
 
   const MediaTrack& audio_track = *(media_tracks_->tracks()[1]);
   EXPECT_EQ(audio_track.type(), MediaTrack::Audio);
   EXPECT_EQ(audio_track.bytestream_track_id(), 2);
-  EXPECT_EQ(audio_track.kind(), "main");
-  EXPECT_EQ(audio_track.label(), "SoundHandler");
-  EXPECT_EQ(audio_track.language(), "und");
+  EXPECT_EQ(audio_track.kind().value(), "main");
+  EXPECT_EQ(audio_track.label().value(), "SoundHandler");
+  EXPECT_EQ(audio_track.language().value(), "und");
 
   const MediaTrack& video_track2 = *(media_tracks_->tracks()[2]);
   EXPECT_EQ(video_track2.type(), MediaTrack::Video);
   EXPECT_EQ(video_track2.bytestream_track_id(), 3);
-  EXPECT_EQ(video_track2.kind(), "main");
-  EXPECT_EQ(video_track2.label(), "VideoHandler");
-  EXPECT_EQ(video_track2.language(), "und");
+  EXPECT_EQ(video_track2.kind().value(), "main");
+  EXPECT_EQ(video_track2.label().value(), "VideoHandler");
+  EXPECT_EQ(video_track2.language().value(), "und");
 
   const MediaTrack& audio_track2 = *(media_tracks_->tracks()[3]);
   EXPECT_EQ(audio_track2.type(), MediaTrack::Audio);
   EXPECT_EQ(audio_track2.bytestream_track_id(), 4);
-  EXPECT_EQ(audio_track2.kind(), "main");
-  EXPECT_EQ(audio_track2.label(), "SoundHandler");
-  EXPECT_EQ(audio_track2.language(), "und");
+  EXPECT_EQ(audio_track2.kind().value(), "main");
+  EXPECT_EQ(audio_track2.label().value(), "SoundHandler");
+  EXPECT_EQ(audio_track2.language().value(), "und");
 }
 
 TEST_F(FFmpegDemuxerTest, Read_Mp4_Crbug657437) {
@@ -1539,16 +1539,16 @@
   const MediaTrack& video_track = *(media_tracks_->tracks()[0]);
   EXPECT_EQ(video_track.type(), MediaTrack::Video);
   EXPECT_EQ(video_track.bytestream_track_id(), 1);
-  EXPECT_EQ(video_track.kind(), "main");
-  EXPECT_EQ(video_track.label(), "");
-  EXPECT_EQ(video_track.language(), "");
+  EXPECT_EQ(video_track.kind().value(), "main");
+  EXPECT_EQ(video_track.label().value(), "");
+  EXPECT_EQ(video_track.language().value(), "");
 
   const MediaTrack& audio_track = *(media_tracks_->tracks()[1]);
   EXPECT_EQ(audio_track.type(), MediaTrack::Audio);
   EXPECT_EQ(audio_track.bytestream_track_id(), 2);
-  EXPECT_EQ(audio_track.kind(), "main");
-  EXPECT_EQ(audio_track.label(), "");
-  EXPECT_EQ(audio_track.language(), "");
+  EXPECT_EQ(audio_track.kind().value(), "main");
+  EXPECT_EQ(audio_track.label().value(), "");
+  EXPECT_EQ(audio_track.language().value(), "");
 }
 
 // UTCDateToTime_* tests here assume FFmpegDemuxer's ExtractTimelineOffset
diff --git a/media/filters/frame_processor_unittest.cc b/media/filters/frame_processor_unittest.cc
index 507f4f8..3afe81a 100644
--- a/media/filters/frame_processor_unittest.cc
+++ b/media/filters/frame_processor_unittest.cc
@@ -343,7 +343,8 @@
     switch (type) {
       case DemuxerStream::AUDIO: {
         ASSERT_FALSE(audio_);
-        audio_.reset(new ChunkDemuxerStream(DemuxerStream::AUDIO, "1"));
+        audio_.reset(
+            new ChunkDemuxerStream(DemuxerStream::AUDIO, MediaTrack::Id("1")));
         AudioDecoderConfig decoder_config(kCodecVorbis, kSampleFormatPlanarF32,
                                           CHANNEL_LAYOUT_STEREO, 1000,
                                           EmptyExtraData(), Unencrypted());
@@ -356,7 +357,8 @@
       }
       case DemuxerStream::VIDEO: {
         ASSERT_FALSE(video_);
-        video_.reset(new ChunkDemuxerStream(DemuxerStream::VIDEO, "2"));
+        video_.reset(
+            new ChunkDemuxerStream(DemuxerStream::VIDEO, MediaTrack::Id("2")));
         ASSERT_TRUE(video_->UpdateVideoConfig(TestVideoConfig::Normal(), false,
                                               &media_log_));
         stream = video_.get();
diff --git a/media/filters/source_buffer_state_unittest.cc b/media/filters/source_buffer_state_unittest.cc
index e99e9a5..a29789a 100644
--- a/media/filters/source_buffer_state_unittest.cc
+++ b/media/filters/source_buffer_state_unittest.cc
@@ -41,11 +41,13 @@
 }
 
 void AddAudioTrack(std::unique_ptr<MediaTracks>& t, AudioCodec codec, int id) {
-  t->AddAudioTrack(CreateAudioConfig(codec), id, "", "", "");
+  t->AddAudioTrack(CreateAudioConfig(codec), id, MediaTrack::Kind(),
+                   MediaTrack::Label(), MediaTrack::Language());
 }
 
 void AddVideoTrack(std::unique_ptr<MediaTracks>& t, VideoCodec codec, int id) {
-  t->AddVideoTrack(CreateVideoConfig(codec, 16, 16), id, "", "", "");
+  t->AddVideoTrack(CreateVideoConfig(codec, 16, 16), id, MediaTrack::Kind(),
+                   MediaTrack::Label(), MediaTrack::Language());
 }
 
 void InvokeCbAndSaveResult(const base::Callback<bool()>& cb, bool* result) {
@@ -141,8 +143,8 @@
 
   ChunkDemuxerStream* CreateDemuxerStream(DemuxerStream::Type type) {
     static unsigned track_id = 0;
-    demuxer_streams_.push_back(base::WrapUnique(
-        new ChunkDemuxerStream(type, base::NumberToString(++track_id))));
+    demuxer_streams_.push_back(base::WrapUnique(new ChunkDemuxerStream(
+        type, MediaTrack::Id(base::NumberToString(++track_id)))));
     return demuxer_streams_.back().get();
   }
 
@@ -231,7 +233,8 @@
   std::unique_ptr<SourceBufferState> sbs =
       CreateAndInitSourceBufferState("opus,vp9");
   std::unique_ptr<MediaTracks> tracks(new MediaTracks());
-  tracks->AddAudioTrack(CreateAudioConfig(kCodecOpus), 1, "", "", "");
+  tracks->AddAudioTrack(CreateAudioConfig(kCodecOpus), 1, MediaTrack::Kind(),
+                        MediaTrack::Label(), MediaTrack::Language());
   EXPECT_MEDIA_LOG(FoundStream("audio"));
   EXPECT_MEDIA_LOG(CodecName("audio", "opus"));
   EXPECT_MEDIA_LOG(InitSegmentMissesExpectedTrack("vp9"));
diff --git a/media/formats/mp2t/mp2t_stream_parser.cc b/media/formats/mp2t/mp2t_stream_parser.cc
index bc281ae..41621a21 100644
--- a/media/formats/mp2t/mp2t_stream_parser.cc
+++ b/media/formats/mp2t/mp2t_stream_parser.cc
@@ -658,12 +658,14 @@
   // TODO(servolk): Implement proper sourcing of media track info as described
   // in crbug.com/590085
   if (audio_config.IsValidConfig()) {
-    media_tracks->AddAudioTrack(audio_config, kMp2tAudioTrackId, "main", "",
-                                "");
+    media_tracks->AddAudioTrack(audio_config, kMp2tAudioTrackId,
+                                MediaTrack::Kind("main"), MediaTrack::Label(""),
+                                MediaTrack::Language(""));
   }
   if (video_config.IsValidConfig()) {
-    media_tracks->AddVideoTrack(video_config, kMp2tVideoTrackId, "main", "",
-                                "");
+    media_tracks->AddVideoTrack(video_config, kMp2tVideoTrackId,
+                                MediaTrack::Kind("main"), MediaTrack::Label(""),
+                                MediaTrack::Language(""));
   }
   return media_tracks;
 }
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc
index 76eb891..71f47b6 100644
--- a/media/formats/mp4/mp4_stream_parser.cc
+++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -469,9 +469,10 @@
       has_audio_ = true;
       audio_track_ids_.insert(audio_track_id);
       const char* track_kind = (audio_track_ids_.size() == 1 ? "main" : "");
-      media_tracks->AddAudioTrack(audio_config, audio_track_id, track_kind,
-                                  track->media.handler.name,
-                                  track->media.header.language());
+      media_tracks->AddAudioTrack(
+          audio_config, audio_track_id, MediaTrack::Kind(track_kind),
+          MediaTrack::Label(track->media.handler.name),
+          MediaTrack::Language(track->media.header.language()));
       continue;
     }
 
@@ -537,10 +538,12 @@
       }
       has_video_ = true;
       video_track_ids_.insert(video_track_id);
-      const char* track_kind = (video_track_ids_.size() == 1 ? "main" : "");
-      media_tracks->AddVideoTrack(video_config, video_track_id, track_kind,
-                                  track->media.handler.name,
-                                  track->media.header.language());
+      auto track_kind =
+          MediaTrack::Kind(video_track_ids_.size() == 1 ? "main" : "");
+      media_tracks->AddVideoTrack(
+          video_config, video_track_id, track_kind,
+          MediaTrack::Label(track->media.handler.name),
+          MediaTrack::Language(track->media.header.language()));
       continue;
     }
 
diff --git a/media/formats/mp4/mp4_stream_parser_unittest.cc b/media/formats/mp4/mp4_stream_parser_unittest.cc
index 7526f17..3c559e96 100644
--- a/media/formats/mp4/mp4_stream_parser_unittest.cc
+++ b/media/formats/mp4/mp4_stream_parser_unittest.cc
@@ -683,16 +683,16 @@
   const MediaTrack& video_track = *(media_tracks_->tracks()[0]);
   EXPECT_EQ(video_track.type(), MediaTrack::Video);
   EXPECT_EQ(video_track.bytestream_track_id(), 1);
-  EXPECT_EQ(video_track.kind(), "main");
-  EXPECT_EQ(video_track.label(), "VideoHandler");
-  EXPECT_EQ(video_track.language(), "und");
+  EXPECT_EQ(video_track.kind().value(), "main");
+  EXPECT_EQ(video_track.label().value(), "VideoHandler");
+  EXPECT_EQ(video_track.language().value(), "und");
 
   const MediaTrack& audio_track = *(media_tracks_->tracks()[1]);
   EXPECT_EQ(audio_track.type(), MediaTrack::Audio);
   EXPECT_EQ(audio_track.bytestream_track_id(), 2);
-  EXPECT_EQ(audio_track.kind(), "main");
-  EXPECT_EQ(audio_track.label(), "SoundHandler");
-  EXPECT_EQ(audio_track.language(), "und");
+  EXPECT_EQ(audio_track.kind().value(), "main");
+  EXPECT_EQ(audio_track.label().value(), "SoundHandler");
+  EXPECT_EQ(audio_track.language().value(), "und");
 }
 
 TEST_F(MP4StreamParserTest, TextTrackDetection) {
@@ -720,30 +720,30 @@
   const MediaTrack& video_track1 = *(media_tracks_->tracks()[0]);
   EXPECT_EQ(video_track1.type(), MediaTrack::Video);
   EXPECT_EQ(video_track1.bytestream_track_id(), 1);
-  EXPECT_EQ(video_track1.kind(), "main");
-  EXPECT_EQ(video_track1.label(), "VideoHandler");
-  EXPECT_EQ(video_track1.language(), "und");
+  EXPECT_EQ(video_track1.kind().value(), "main");
+  EXPECT_EQ(video_track1.label().value(), "VideoHandler");
+  EXPECT_EQ(video_track1.language().value(), "und");
 
   const MediaTrack& audio_track1 = *(media_tracks_->tracks()[1]);
   EXPECT_EQ(audio_track1.type(), MediaTrack::Audio);
   EXPECT_EQ(audio_track1.bytestream_track_id(), 2);
-  EXPECT_EQ(audio_track1.kind(), "main");
-  EXPECT_EQ(audio_track1.label(), "SoundHandler");
-  EXPECT_EQ(audio_track1.language(), "und");
+  EXPECT_EQ(audio_track1.kind().value(), "main");
+  EXPECT_EQ(audio_track1.label().value(), "SoundHandler");
+  EXPECT_EQ(audio_track1.language().value(), "und");
 
   const MediaTrack& video_track2 = *(media_tracks_->tracks()[2]);
   EXPECT_EQ(video_track2.type(), MediaTrack::Video);
   EXPECT_EQ(video_track2.bytestream_track_id(), 3);
-  EXPECT_EQ(video_track2.kind(), "");
-  EXPECT_EQ(video_track2.label(), "VideoHandler");
-  EXPECT_EQ(video_track2.language(), "und");
+  EXPECT_EQ(video_track2.kind().value(), "");
+  EXPECT_EQ(video_track2.label().value(), "VideoHandler");
+  EXPECT_EQ(video_track2.language().value(), "und");
 
   const MediaTrack& audio_track2 = *(media_tracks_->tracks()[3]);
   EXPECT_EQ(audio_track2.type(), MediaTrack::Audio);
   EXPECT_EQ(audio_track2.bytestream_track_id(), 4);
-  EXPECT_EQ(audio_track2.kind(), "");
-  EXPECT_EQ(audio_track2.label(), "SoundHandler");
-  EXPECT_EQ(audio_track2.language(), "und");
+  EXPECT_EQ(audio_track2.kind().value(), "");
+  EXPECT_EQ(audio_track2.label().value(), "SoundHandler");
+  EXPECT_EQ(audio_track2.language().value(), "und");
 }
 
 // <cos(θ), sin(θ), θ expressed as a rotation Enum>
diff --git a/media/formats/mpeg/mpeg_audio_stream_parser_base.cc b/media/formats/mpeg/mpeg_audio_stream_parser_base.cc
index cea199095..137bfa4 100644
--- a/media/formats/mpeg/mpeg_audio_stream_parser_base.cc
+++ b/media/formats/mpeg/mpeg_audio_stream_parser_base.cc
@@ -225,7 +225,9 @@
 
     std::unique_ptr<MediaTracks> media_tracks(new MediaTracks());
     if (config_.IsValidConfig()) {
-      media_tracks->AddAudioTrack(config_, kMpegAudioTrackId, "main", "", "");
+      media_tracks->AddAudioTrack(config_, kMpegAudioTrackId,
+                                  MediaTrack::Kind("main"), MediaTrack::Label(),
+                                  MediaTrack::Language());
     }
     if (!config_cb_.Run(std::move(media_tracks), TextTrackConfigMap()))
       return -1;
diff --git a/media/formats/webm/webm_stream_parser_unittest.cc b/media/formats/webm/webm_stream_parser_unittest.cc
index c6180d6273..0b1a3b8 100644
--- a/media/formats/webm/webm_stream_parser_unittest.cc
+++ b/media/formats/webm/webm_stream_parser_unittest.cc
@@ -107,16 +107,16 @@
   const MediaTrack& video_track = *(media_tracks_->tracks()[0]);
   EXPECT_EQ(video_track.type(), MediaTrack::Video);
   EXPECT_EQ(video_track.bytestream_track_id(), 1);
-  EXPECT_EQ(video_track.kind(), "main");
-  EXPECT_EQ(video_track.label(), "");
-  EXPECT_EQ(video_track.language(), "und");
+  EXPECT_EQ(video_track.kind().value(), "main");
+  EXPECT_EQ(video_track.label().value(), "");
+  EXPECT_EQ(video_track.language().value(), "und");
 
   const MediaTrack& audio_track = *(media_tracks_->tracks()[1]);
   EXPECT_EQ(audio_track.type(), MediaTrack::Audio);
   EXPECT_EQ(audio_track.bytestream_track_id(), 2);
-  EXPECT_EQ(audio_track.kind(), "main");
-  EXPECT_EQ(audio_track.label(), "");
-  EXPECT_EQ(audio_track.language(), "und");
+  EXPECT_EQ(audio_track.kind().value(), "main");
+  EXPECT_EQ(audio_track.label().value(), "");
+  EXPECT_EQ(audio_track.language().value(), "und");
 }
 
 TEST_F(WebMStreamParserTest, VerifyDetectedTrack_AudioOnly) {
diff --git a/media/formats/webm/webm_tracks_parser.cc b/media/formats/webm/webm_tracks_parser.cc
index 22db0c7..6ae7ce8 100644
--- a/media/formats/webm/webm_tracks_parser.cc
+++ b/media/formats/webm/webm_tracks_parser.cc
@@ -229,8 +229,9 @@
         }
         media_tracks_->AddAudioTrack(
             audio_decoder_config_,
-            static_cast<StreamParser::TrackId>(track_num_), "main", track_name_,
-            track_language_);
+            static_cast<StreamParser::TrackId>(track_num_),
+            MediaTrack::Kind("main"), MediaTrack::Label(track_name_),
+            MediaTrack::Language(track_language_));
       } else {
         MEDIA_LOG(DEBUG, media_log_) << "Ignoring audio track " << track_num_;
         ignored_tracks_.insert(track_num_);
@@ -256,8 +257,9 @@
         }
         media_tracks_->AddVideoTrack(
             video_decoder_config_,
-            static_cast<StreamParser::TrackId>(track_num_), "main", track_name_,
-            track_language_);
+            static_cast<StreamParser::TrackId>(track_num_),
+            MediaTrack::Kind("main"), MediaTrack::Label(track_name_),
+            MediaTrack::Language(track_language_));
       } else {
         MEDIA_LOG(DEBUG, media_log_) << "Ignoring video track " << track_num_;
         ignored_tracks_.insert(track_num_);
diff --git a/media/gpu/test/video_frame_file_writer.h b/media/gpu/test/video_frame_file_writer.h
index eda9f24..92c4f24 100644
--- a/media/gpu/test/video_frame_file_writer.h
+++ b/media/gpu/test/video_frame_file_writer.h
@@ -21,10 +21,6 @@
 
 namespace test {
 
-// Default output folder used to store frames.
-constexpr const base::FilePath::CharType* kDefaultOutputFolder =
-    FILE_PATH_LITERAL("video_frames");
-
 // The video frame file writer class implements functionality to write video
 // frames to file. The supported output formats are PNG and raw I420 YUV.
 class VideoFrameFileWriter : public VideoFrameProcessor {
@@ -39,8 +35,7 @@
 
   // Create an instance of the video frame file writer.
   static std::unique_ptr<VideoFrameFileWriter> Create(
-      const base::FilePath& output_folder =
-          base::FilePath(kDefaultOutputFolder),
+      const base::FilePath& output_folder,
       OutputFormat output_format = OutputFormat::kPNG);
 
   // Interface VideoFrameProcessor
diff --git a/media/gpu/test/video_player/video_player_test_environment.cc b/media/gpu/test/video_player/video_player_test_environment.cc
index 9d5b4b5..895b2999 100644
--- a/media/gpu/test/video_player/video_player_test_environment.cc
+++ b/media/gpu/test/video_player/video_player_test_environment.cc
@@ -21,6 +21,7 @@
     const base::FilePath& video_metadata_path,
     bool enable_validator,
     bool output_frames,
+    const base::FilePath& output_folder,
     bool use_vd) {
   auto video = std::make_unique<media::test::Video>(
       video_path.empty() ? base::FilePath(kDefaultTestVideoPath) : video_path,
@@ -31,17 +32,19 @@
   }
 
   return new VideoPlayerTestEnvironment(std::move(video), enable_validator,
-                                        output_frames, use_vd);
+                                        output_frames, output_folder, use_vd);
 }
 
 VideoPlayerTestEnvironment::VideoPlayerTestEnvironment(
     std::unique_ptr<media::test::Video> video,
     bool enable_validator,
     bool output_frames,
+    const base::FilePath& output_folder,
     bool use_vd)
     : video_(std::move(video)),
       enable_validator_(enable_validator),
       output_frames_(output_frames),
+      output_folder_(output_folder),
       use_vd_(use_vd) {}
 
 VideoPlayerTestEnvironment::~VideoPlayerTestEnvironment() = default;
@@ -58,6 +61,10 @@
   return output_frames_;
 }
 
+const base::FilePath& VideoPlayerTestEnvironment::OutputFolder() const {
+  return output_folder_;
+}
+
 bool VideoPlayerTestEnvironment::UseVD() const {
   return use_vd_;
 }
diff --git a/media/gpu/test/video_player/video_player_test_environment.h b/media/gpu/test/video_player/video_player_test_environment.h
index db9b92b9..5507c8d 100644
--- a/media/gpu/test/video_player/video_player_test_environment.h
+++ b/media/gpu/test/video_player/video_player_test_environment.h
@@ -24,6 +24,7 @@
       const base::FilePath& video_metadata_path,
       bool enable_validator,
       bool output_frames,
+      const base::FilePath& output_folder,
       bool use_vd);
   ~VideoPlayerTestEnvironment() override;
 
@@ -33,6 +34,8 @@
   bool IsValidatorEnabled() const;
   // Check whether outputting frames is enabled.
   bool IsFramesOutputEnabled() const;
+  // Get the output folder.
+  const base::FilePath& OutputFolder() const;
   // Check whether we should use VD-based video decoders instead of VDA-based.
   bool UseVD() const;
 
@@ -40,11 +43,13 @@
   VideoPlayerTestEnvironment(std::unique_ptr<media::test::Video> video,
                              bool enable_validator,
                              bool output_frames,
+                             const base::FilePath& output_folder,
                              bool use_vd);
 
   const std::unique_ptr<media::test::Video> video_;
   const bool enable_validator_;
   const bool output_frames_;
+  const base::FilePath output_folder_;
   const bool use_vd_;
 };
 }  // namespace test
diff --git a/media/gpu/test/video_test_environment.cc b/media/gpu/test/video_test_environment.cc
index 3d1a2ad..db830a1 100644
--- a/media/gpu/test/video_test_environment.cc
+++ b/media/gpu/test/video_test_environment.cc
@@ -28,6 +28,11 @@
 VideoTestEnvironment::~VideoTestEnvironment() = default;
 
 void VideoTestEnvironment::SetUp() {
+  // If using '--gtest_repeat' Setup/TearDown will be called multiple times on
+  // the same environment, however the setup here should only be performed once.
+  if (initialized_)
+    return;
+
   // Using shared memory requires mojo to be initialized (crbug.com/849207).
   mojo::core::Init();
 
@@ -67,10 +72,11 @@
   gpu_helper_.reset(new ui::OzoneGpuTestHelper());
   gpu_helper_->Initialize(base::ThreadTaskRunnerHandle::Get());
 #endif
+
+  initialized_ = true;
 }
 
 void VideoTestEnvironment::TearDown() {
-  task_environment_.reset();
 }
 
 base::FilePath::StringType VideoTestEnvironment::GetTestName() const {
diff --git a/media/gpu/test/video_test_environment.h b/media/gpu/test/video_test_environment.h
index 33ce0e4..76838d7 100644
--- a/media/gpu/test/video_test_environment.h
+++ b/media/gpu/test/video_test_environment.h
@@ -42,6 +42,9 @@
   base::FilePath::StringType GetTestName() const;
 
  private:
+  // Whether the test environment has been initialized.
+  bool initialized_ = false;
+
   // An exit manager is required to run callbacks on shutdown.
   base::AtExitManager at_exit_manager;
 
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn
index d756ef22..906be540 100644
--- a/media/gpu/v4l2/BUILD.gn
+++ b/media/gpu/v4l2/BUILD.gn
@@ -39,6 +39,8 @@
     "v4l2_image_processor.h",
     "v4l2_slice_video_decode_accelerator.cc",
     "v4l2_slice_video_decode_accelerator.h",
+    "v4l2_stateful_workaround.cc",
+    "v4l2_stateful_workaround.h",
     "v4l2_video_decode_accelerator.cc",
     "v4l2_video_decode_accelerator.h",
     "v4l2_video_encode_accelerator.cc",
diff --git a/media/gpu/v4l2/v4l2_stateful_workaround.cc b/media/gpu/v4l2/v4l2_stateful_workaround.cc
new file mode 100644
index 0000000..221856a
--- /dev/null
+++ b/media/gpu/v4l2/v4l2_stateful_workaround.cc
@@ -0,0 +1,131 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/v4l2/v4l2_stateful_workaround.h"
+
+#include <string.h>
+
+#include <linux/videodev2.h>
+
+#include "base/containers/small_map.h"
+#include "base/memory/ptr_util.h"
+#include "media/gpu/macros.h"
+#include "media/parsers/vp8_parser.h"
+#include "media/video/video_decode_accelerator.h"
+
+namespace media {
+
+// If the given resolution is not supported by the driver, some IOCTL must
+// return some error code (e.g. EIO). However, there is a driver that doesn't
+// follow this specification, for example go2001. This will be called before
+// a bitstream to the driver in the driver. This parses the bitstream, gets
+// its resolution and compares with the supported resolution.
+// Returns true if the resolution is supported or this workaround is
+// unnecessary. Otherwise return false.
+// This class is currently created only on guado when codec is VP8.
+// TODO(crbug.com/968945): Check this workaround is necessary for other codecs
+// and other devices.
+class SupportResolutionChecker : public V4L2StatefulWorkaround {
+ public:
+  static std::unique_ptr<V4L2StatefulWorkaround> CreateIfNeeded(
+      V4L2Device::Type device_type,
+      VideoCodecProfile profile);
+  ~SupportResolutionChecker() override = default;
+
+  Result Apply(const uint8_t* data, size_t size, size_t* endpos) override;
+
+ private:
+  using SupportedProfileMap = base::small_map<
+      std::map<VideoCodecProfile, VideoDecodeAccelerator::SupportedProfile>>;
+
+  SupportResolutionChecker(SupportedProfileMap supported_profile_map)
+      : supported_profile_map_(std::move(supported_profile_map)),
+        vp8_parser_(std::make_unique<Vp8Parser>()) {}
+
+  SupportedProfileMap supported_profile_map_;
+  const std::unique_ptr<Vp8Parser> vp8_parser_;
+};
+
+std::unique_ptr<V4L2StatefulWorkaround>
+SupportResolutionChecker::CreateIfNeeded(V4L2Device::Type device_type,
+                                         VideoCodecProfile profile) {
+  scoped_refptr<V4L2Device> device = V4L2Device::Create();
+  if (device_type == V4L2Device::Type::kDecoder && profile >= VP8PROFILE_MIN &&
+      profile <= VP8PROFILE_MAX) {
+    if (!device->Open(V4L2Device::Type::kDecoder, V4L2_PIX_FMT_VP8)) {
+      VPLOGF(1) << "Failed to open device for profile: " << profile
+                << " fourcc: " << FourccToString(V4L2_PIX_FMT_VP8);
+      return nullptr;
+    }
+
+    // Get the driver name.
+    struct v4l2_capability caps;
+    if (device->Ioctl(VIDIOC_QUERYCAP, &caps) != 0) {
+      VPLOGF(1) << "ioctl() failed: VIDIOC_QUERYCAP"
+                << ", caps check failed: 0x" << std::hex << caps.capabilities;
+      return nullptr;
+    }
+    constexpr char go2001[] = "go2001";
+    if (strcmp(reinterpret_cast<const char*>(caps.driver), go2001))
+      return nullptr;
+  }
+
+  constexpr uint32_t supported_input_fourccs[] = {
+      V4L2_PIX_FMT_VP8,
+  };
+  auto supported_profiles = device->GetSupportedDecodeProfiles(
+      base::size(supported_input_fourccs), supported_input_fourccs);
+  SupportedProfileMap supported_profile_map;
+  for (const auto& profile : supported_profiles)
+    supported_profile_map[profile.profile] = profile;
+
+  VLOGF(2) << "Create SupportResolutionChecker workaround";
+  return base::WrapUnique(
+      new SupportResolutionChecker(std::move(supported_profile_map)));
+}
+
+V4L2StatefulWorkaround::Result SupportResolutionChecker::Apply(
+    const uint8_t* data,
+    size_t size,
+    size_t* endpos) {
+  Vp8FrameHeader fhdr;
+  vp8_parser_->ParseFrame(data, size, &fhdr);
+  if (fhdr.IsKeyframe()) {
+    DCHECK(supported_profile_map_.find(VP8PROFILE_ANY) !=
+           supported_profile_map_.end());
+    const auto& supported_profile = supported_profile_map_[VP8PROFILE_ANY];
+    const auto& min_resolution = supported_profile.min_resolution;
+    const auto& max_resolution = supported_profile.max_resolution;
+    const gfx::Rect current_resolution(fhdr.width, fhdr.height);
+    if (!gfx::Rect(max_resolution).Contains(current_resolution) ||
+        !(current_resolution).Contains(gfx::Rect(min_resolution))) {
+      VLOGF(1) << "Resolution is unsupported: "
+               << current_resolution.size().ToString()
+               << ", min supported resolution: " << min_resolution.ToString()
+               << ", max supported resolution: " << max_resolution.ToString();
+      return Result::NotifyError;
+    }
+  }
+  return Result::Success;
+}
+
+std::vector<std::unique_ptr<V4L2StatefulWorkaround>>
+CreateV4L2StatefulWorkarounds(V4L2Device::Type device_type,
+                              VideoCodecProfile profile) {
+  using CreateWorkaroundFuncType = std::unique_ptr<V4L2StatefulWorkaround> (*)(
+      V4L2Device::Type device_type, VideoCodecProfile profile);
+  const CreateWorkaroundFuncType kWorkaroundFactoryFunction[] = {
+      &SupportResolutionChecker::CreateIfNeeded,
+  };
+
+  std::vector<std::unique_ptr<V4L2StatefulWorkaround>> workarounds;
+  for (const auto func : kWorkaroundFactoryFunction) {
+    auto vw = func(device_type, profile);
+    if (vw)
+      workarounds.push_back(std::move(vw));
+  }
+  return workarounds;
+}
+
+}  // namespace media
diff --git a/media/gpu/v4l2/v4l2_stateful_workaround.h b/media/gpu/v4l2/v4l2_stateful_workaround.h
new file mode 100644
index 0000000..c0a746a
--- /dev/null
+++ b/media/gpu/v4l2/v4l2_stateful_workaround.h
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_V4L2_V4L2_STATEFUL_WORKAROUND_H_
+#define MEDIA_GPU_V4L2_V4L2_STATEFUL_WORKAROUND_H_
+
+#include <memory>
+#include <vector>
+
+#include "media/base/video_types.h"
+#include "media/gpu/v4l2/v4l2_device.h"
+
+namespace media {
+
+class V4L2StatefulWorkaround {
+ public:
+  enum class Result {
+    Success,      // The workaround is successfully applied.
+    NotifyError,  // The caller must notify an error for Chrome. For example,
+                  // VDA will call NotifyError() if this is returned.
+  };
+
+  virtual ~V4L2StatefulWorkaround() = default;
+
+  // Apply the workaround.
+  virtual Result Apply(const uint8_t* data, size_t size, size_t* endpos) = 0;
+
+ protected:
+  V4L2StatefulWorkaround() = default;
+
+  DISALLOW_COPY_AND_ASSIGN(V4L2StatefulWorkaround);
+};
+
+// Create necessary workarounds on the device for |device_type| and |profile|.
+std::vector<std::unique_ptr<V4L2StatefulWorkaround>>
+CreateV4L2StatefulWorkarounds(V4L2Device::Type device_type,
+                              VideoCodecProfile profile);
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_V4L2_V4L2_STATEFUL_WORKAROUND_H_
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
index 7e55c00..7bd8ca6 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
@@ -34,6 +34,7 @@
 #include "media/gpu/image_processor_factory.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/v4l2/v4l2_image_processor.h"
+#include "media/gpu/v4l2/v4l2_stateful_workaround.h"
 #include "media/video/h264_parser.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gl/gl_context.h"
@@ -317,6 +318,9 @@
     return false;
   }
 
+  workarounds_ =
+      CreateV4L2StatefulWorkarounds(V4L2Device::Type::kDecoder, config.profile);
+
   output_mode_ = config.output_mode;
 
   input_queue_ = device_->GetQueue(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
@@ -981,6 +985,14 @@
                                                       size_t* endpos) {
   DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
 
+  for (auto& workaround : workarounds_) {
+    auto result = workaround->Apply(data, size, endpos);
+    if (result == V4L2StatefulWorkaround::Result::NotifyError) {
+      NOTIFY_ERROR(PLATFORM_FAILURE);
+      return false;
+    }
+  }
+
   if (video_profile_ >= H264PROFILE_MIN && video_profile_ <= H264PROFILE_MAX) {
     // For H264, we need to feed HW one frame at a time.  This is going to take
     // some parsing of our input stream.
@@ -1930,6 +1942,7 @@
   output_queue_ = nullptr;
 
   decoder_h264_parser_ = nullptr;
+  workarounds_.clear();
 
   base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
       this);
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.h b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
index c66c1bd9..e42749a 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
@@ -47,6 +47,7 @@
 namespace media {
 
 class H264Parser;
+class V4L2StatefulWorkaround;
 
 // This class handles video accelerators directly through a V4L2 device exported
 // by the hardware blocks.
@@ -507,6 +508,11 @@
   // For H264 decode, hardware requires that we send it frame-sized chunks.
   // We'll need to parse the stream.
   std::unique_ptr<H264Parser> decoder_h264_parser_;
+
+  // Workaround for V4L2VideoDecodeAccelerator. This is created only if some
+  // workaround is necessary for the V4L2VideoDecodeAccelerator.
+  std::vector<std::unique_ptr<V4L2StatefulWorkaround>> workarounds_;
+
   // Set if the decoder has a pending incomplete frame in an input buffer.
   bool decoder_partial_frame_pending_;
 
diff --git a/media/gpu/video_decode_accelerator_perf_tests.cc b/media/gpu/video_decode_accelerator_perf_tests.cc
index 54aec2c..8cd9b39 100644
--- a/media/gpu/video_decode_accelerator_perf_tests.cc
+++ b/media/gpu/video_decode_accelerator_perf_tests.cc
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <algorithm>
+#include <numeric>
 #include <vector>
 
 #include "base/command_line.h"
 #include "base/files/file_util.h"
+#include "base/json/json_writer.h"
 #include "base/strings/stringprintf.h"
 #include "media/base/test_data_util.h"
 #include "media/gpu/test/video_player/frame_renderer_dummy.h"
@@ -25,8 +28,9 @@
 // making changes here.
 constexpr const char* usage_msg =
     "usage: video_decode_accelerator_perf_tests\n"
-    "           [-v=<level>] [--vmodule=<config>] [--use_vd] [--gtest_help]\n"
-    "           [--help] [<video path>] [<video metadata path>]\n";
+    "           [-v=<level>] [--vmodule=<config>] [--output_folder]\n"
+    "           [--use_vd] [--gtest_help] [--help]\n"
+    "           [<video path>] [<video metadata path>]\n";
 
 // Video decoder perf tests help message.
 constexpr const char* help_msg =
@@ -40,6 +44,9 @@
     "   -v                  enable verbose mode, e.g. -v=2.\n"
     "  --vmodule            enable verbose mode for the specified module,\n"
     "                       e.g. --vmodule=*media/gpu*=2.\n"
+    "  --output_folder      overwrite the output folder used to store\n"
+    "                       performance metrics, if not specified results\n"
+    "                       will be stored in the current working directory.\n"
     "  --use_vd             use the new VD-based video decoders, instead of\n"
     "                       the default VDA-based video decoders.\n"
     "  --gtest_help         display the gtest help and exit.\n"
@@ -51,7 +58,25 @@
 constexpr const base::FilePath::CharType* kDefaultOutputFolder =
     FILE_PATH_LITERAL("perf_metrics");
 
-// TODO(dstaessens@) Expand with more meaningful metrics.
+// Struct storing various time-related statistics.
+struct PerformanceTimeStats {
+  PerformanceTimeStats() {}
+  explicit PerformanceTimeStats(const std::vector<double>& times);
+  double avg_ms_ = 0.0;
+  double percentile_25_ms_ = 0.0;
+  double percentile_50_ms_ = 0.0;
+  double percentile_75_ms_ = 0.0;
+};
+
+PerformanceTimeStats::PerformanceTimeStats(const std::vector<double>& times) {
+  avg_ms_ = std::accumulate(times.begin(), times.end(), 0.0) / times.size();
+  std::vector<double> sorted_times = times;
+  std::sort(sorted_times.begin(), sorted_times.end());
+  percentile_25_ms_ = sorted_times[sorted_times.size() / 4];
+  percentile_50_ms_ = sorted_times[sorted_times.size() / 2];
+  percentile_75_ms_ = sorted_times[(sorted_times.size() * 3) / 4];
+}
+
 struct PerformanceMetrics {
   // Total measurement duration.
   base::TimeDelta total_duration_;
@@ -62,10 +87,12 @@
   // The number of frames dropped because of the decoder running behind, only
   // relevant for capped performance tests.
   size_t frames_dropped_ = 0;
-  // The average time between subsequent frame deliveries.
-  double avg_frame_delivery_time_ms_ = 0.0;
-  // The median time between decode start and frame delivery.
-  double median_frame_decode_time_ms_ = 0.0;
+  // The rate at which frames are dropped: dropped frames / non-dropped frames.
+  double dropped_frame_rate_ = 0;
+  // Statistics about the time between subsequent frame deliveries.
+  PerformanceTimeStats delivery_time_stats_;
+  // Statistics about the time between decode start and frame deliveries.
+  PerformanceTimeStats decode_time_stats_;
 };
 
 // The performance evaluator can be plugged into the video player to collect
@@ -138,67 +165,124 @@
                                      perf_metrics_.total_duration_.InSecondsF();
   perf_metrics_.frames_dropped_ = frame_renderer_->FramesDropped();
 
-  perf_metrics_.avg_frame_delivery_time_ms_ =
-      perf_metrics_.total_duration_.InMillisecondsF() /
-      perf_metrics_.frames_decoded_;
+  // Calculate the frame drop rate.
+  // TODO(dstaessens@) Find a better metric for dropped frames.
+  size_t frames_rendered =
+      perf_metrics_.frames_decoded_ - perf_metrics_.frames_dropped_;
+  perf_metrics_.dropped_frame_rate_ =
+      perf_metrics_.frames_dropped_ / std::max<size_t>(frames_rendered, 1ul);
 
-  std::sort(frame_decode_times_.begin(), frame_decode_times_.end());
-  size_t median_index = frame_decode_times_.size() / 2;
-  perf_metrics_.median_frame_decode_time_ms_ =
-      (frame_decode_times_.size() % 2 != 0)
-          ? frame_decode_times_[median_index]
-          : (frame_decode_times_[median_index - 1] +
-             frame_decode_times_[median_index]) /
-                2.0;
+  // Calculate delivery and decode time metrics.
+  perf_metrics_.delivery_time_stats_ =
+      PerformanceTimeStats(frame_delivery_times_);
+  perf_metrics_.decode_time_stats_ = PerformanceTimeStats(frame_decode_times_);
 
-  VLOG(0) << "Frames decoded: " << perf_metrics_.frames_decoded_;
-  VLOG(0) << "Total duration: "
-          << perf_metrics_.total_duration_.InMillisecondsF() << "ms";
-  VLOG(0) << "FPS:            " << perf_metrics_.frames_per_second_;
-  VLOG(0) << "Frames Dropped: " << perf_metrics_.frames_dropped_;
-  VLOG(0) << "Avg. frame delivery time:   "
-          << perf_metrics_.avg_frame_delivery_time_ms_ << "ms";
-  VLOG(0) << "Median frame decode time:   "
-          << perf_metrics_.median_frame_decode_time_ms_ << "ms";
+  std::cout << "Frames decoded:     " << perf_metrics_.frames_decoded_
+            << std::endl;
+  std::cout << "Total duration:     "
+            << perf_metrics_.total_duration_.InMillisecondsF() << "ms"
+            << std::endl;
+  std::cout << "FPS:                " << perf_metrics_.frames_per_second_
+            << std::endl;
+  std::cout << "Frames Dropped:     " << perf_metrics_.frames_dropped_
+            << std::endl;
+  std::cout << "Dropped frame rate: " << perf_metrics_.dropped_frame_rate_
+            << std::endl;
+  std::cout << "Frame delivery time - average:       "
+            << perf_metrics_.delivery_time_stats_.avg_ms_ << "ms" << std::endl;
+  std::cout << "Frame delivery time - percentile 25: "
+            << perf_metrics_.delivery_time_stats_.percentile_25_ms_ << "ms"
+            << std::endl;
+  std::cout << "Frame delivery time - percentile 50: "
+            << perf_metrics_.delivery_time_stats_.percentile_50_ms_ << "ms"
+            << std::endl;
+  std::cout << "Frame delivery time - percentile 75: "
+            << perf_metrics_.delivery_time_stats_.percentile_75_ms_ << "ms"
+            << std::endl;
+  std::cout << "Frame decode time - average:       "
+            << perf_metrics_.decode_time_stats_.avg_ms_ << "ms" << std::endl;
+  std::cout << "Frame decode time - percentile 25: "
+            << perf_metrics_.decode_time_stats_.percentile_25_ms_ << "ms"
+            << std::endl;
+  std::cout << "Frame decode time - percentile 50: "
+            << perf_metrics_.decode_time_stats_.percentile_50_ms_ << "ms"
+            << std::endl;
+  std::cout << "Frame decode time - percentile 75: "
+            << perf_metrics_.decode_time_stats_.percentile_75_ms_ << "ms"
+            << std::endl;
 }
 
 void PerformanceEvaluator::WriteMetricsToFile() const {
-  std::string str = base::StringPrintf(
-      "Frames decoded: %zu\nTotal duration: %fms\nFPS: %f\n"
-      "Frames dropped: %zu\nAvg. frame delivery time: %fms\n"
-      "Median frame decode time: %fms\n",
-      perf_metrics_.frames_decoded_,
-      perf_metrics_.total_duration_.InMillisecondsF(),
-      perf_metrics_.frames_per_second_, perf_metrics_.frames_dropped_,
-      perf_metrics_.avg_frame_delivery_time_ms_,
-      perf_metrics_.median_frame_decode_time_ms_);
-
-  // Write performance metrics to file.
-  base::FilePath output_folder_path = base::FilePath(kDefaultOutputFolder);
+  base::FilePath output_folder_path = base::FilePath(g_env->OutputFolder());
   if (!DirectoryExists(output_folder_path))
     base::CreateDirectory(output_folder_path);
-  base::FilePath output_file_path =
-      output_folder_path.Append(base::FilePath(g_env->GetTestName())
-                                    .AddExtension(FILE_PATH_LITERAL(".txt")));
-  base::File output_file(
-      base::FilePath(output_file_path),
-      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-  output_file.WriteAtCurrentPos(str.data(), str.length());
-  VLOG(0) << "Wrote performance metrics to: " << output_file_path;
+  output_folder_path = base::MakeAbsoluteFilePath(output_folder_path);
 
-  // Write frame decode times to file.
-  base::FilePath decode_times_file_path =
-      output_file_path.InsertBeforeExtension(FILE_PATH_LITERAL(".frame_times"));
-  base::File decode_times_output_file(
-      base::FilePath(decode_times_file_path),
-      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-  for (double frame_decoded_time : frame_delivery_times_) {
-    std::string decode_time_str =
-        base::StringPrintf("%f\n", frame_decoded_time);
-    decode_times_output_file.WriteAtCurrentPos(decode_time_str.data(),
-                                               decode_time_str.length());
+  // Write performance metrics to json.
+  base::Value metrics(base::Value::Type::DICTIONARY);
+  metrics.SetKey(
+      "FramesDecoded",
+      base::Value(base::checked_cast<int>(perf_metrics_.frames_decoded_)));
+  metrics.SetKey("TotalDurationMs",
+                 base::Value(perf_metrics_.total_duration_.InMillisecondsF()));
+  metrics.SetKey("FPS", base::Value(perf_metrics_.frames_per_second_));
+  metrics.SetKey(
+      "FramesDropped",
+      base::Value(base::checked_cast<int>(perf_metrics_.frames_dropped_)));
+  metrics.SetKey("DroppedFrameRate",
+                 base::Value(perf_metrics_.dropped_frame_rate_));
+  metrics.SetKey("FrameDeliveryTimeAverage",
+                 base::Value(perf_metrics_.delivery_time_stats_.avg_ms_));
+  metrics.SetKey(
+      "FrameDeliveryTimePercentile25",
+      base::Value(perf_metrics_.delivery_time_stats_.percentile_25_ms_));
+  metrics.SetKey(
+      "FrameDeliveryTimePercentile50",
+      base::Value(perf_metrics_.delivery_time_stats_.percentile_50_ms_));
+  metrics.SetKey(
+      "FrameDeliveryTimePercentile75",
+      base::Value(perf_metrics_.delivery_time_stats_.percentile_75_ms_));
+  metrics.SetKey("FrameDecodeTimeAverage",
+                 base::Value(perf_metrics_.decode_time_stats_.avg_ms_));
+  metrics.SetKey(
+      "FrameDecodeTimePercentile25",
+      base::Value(perf_metrics_.decode_time_stats_.percentile_25_ms_));
+  metrics.SetKey(
+      "FrameDecodeTimePercentile50",
+      base::Value(perf_metrics_.decode_time_stats_.percentile_50_ms_));
+  metrics.SetKey(
+      "FrameDecodeTimePercentile75",
+      base::Value(perf_metrics_.decode_time_stats_.percentile_75_ms_));
+
+  // Write frame delivery times to json.
+  base::Value delivery_times(base::Value::Type::LIST);
+  for (double frame_delivery_time : frame_delivery_times_) {
+    delivery_times.GetList().emplace_back(frame_delivery_time);
   }
-  VLOG(0) << "Wrote frame decode times to: " << decode_times_file_path;
+  metrics.SetKey("FrameDeliveryTimes", std::move(delivery_times));
+
+  // Write frame decodes times to json.
+  base::Value decode_times(base::Value::Type::LIST);
+  for (double frame_decode_time : frame_decode_times_) {
+    decode_times.GetList().emplace_back(frame_decode_time);
+  }
+  metrics.SetKey("FrameDecodeTimes", std::move(decode_times));
+
+  // Write json to file.
+  std::string metrics_str;
+  ASSERT_TRUE(base::JSONWriter::WriteWithOptions(
+      metrics, base::JSONWriter::OPTIONS_PRETTY_PRINT, &metrics_str));
+
+  base::FilePath metrics_file_path =
+      output_folder_path.Append(base::FilePath(g_env->GetTestName())
+                                    .AddExtension(FILE_PATH_LITERAL(".json")));
+  base::File metrics_output_file(
+      base::FilePath(metrics_file_path),
+      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+  int bytes_written = metrics_output_file.WriteAtCurrentPos(
+      metrics_str.data(), metrics_str.length());
+  ASSERT_EQ(bytes_written, static_cast<int>(metrics_str.length()));
+  VLOG(0) << "Wrote performance metrics to: " << metrics_file_path;
 }
 
 // Video decode test class. Performs setup and teardown for each single test.
@@ -300,6 +384,7 @@
       (args.size() >= 2) ? base::FilePath(args[1]) : base::FilePath();
 
   // Parse command line arguments.
+  base::FilePath::StringType output_folder = media::test::kDefaultOutputFolder;
   bool use_vd = false;
   base::CommandLine::SwitchMap switches = cmd_line->GetSwitches();
   for (base::CommandLine::SwitchMap::const_iterator it = switches.begin();
@@ -309,7 +394,9 @@
       continue;
     }
 
-    if (it->first == "use_vd") {
+    if (it->first == "output_folder") {
+      output_folder = it->second;
+    } else if (it->first == "use_vd") {
       use_vd = true;
     } else {
       std::cout << "unknown option: --" << it->first << "\n"
@@ -323,7 +410,8 @@
   // Set up our test environment.
   media::test::VideoPlayerTestEnvironment* test_environment =
       media::test::VideoPlayerTestEnvironment::Create(
-          video_path, video_metadata_path, false, false, use_vd);
+          video_path, video_metadata_path, false, false,
+          base::FilePath(output_folder), use_vd);
   if (!test_environment)
     return EXIT_FAILURE;
 
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index 553b34b..265185f 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -24,8 +24,8 @@
 constexpr const char* usage_msg =
     "usage: video_decode_accelerator_tests\n"
     "           [-v=<level>] [--vmodule=<config>] [--disable_validator]\n"
-    "           [--output_frames] [--use_vd] [--gtest_help] [--help]\n"
-    "           [<video path>] [<video metadata path>]\n";
+    "           [--output_frames] [output_folder] [--use_vd] [--gtest_help]\n"
+    "           [--help] [<video path>] [<video metadata path>]\n";
 
 // Video decoder tests help message.
 constexpr const char* help_msg =
@@ -43,6 +43,8 @@
     "                       platforms that don't support import mode.\n"
     "  --output_frames      write all decoded video frames to the\n"
     "                       \"video_frames\" folder.\n"
+    "  --output_folder      overwrite the default output folder used when\n"
+    "                       \"--output_frames\" is specified.\n"
     "  --use_vd             use the new VD-based video decoders, instead of\n"
     "                       the default VDA-based video decoders.\n"
     "  --gtest_help         display the gtest help and exit.\n"
@@ -50,6 +52,10 @@
 
 media::test::VideoPlayerTestEnvironment* g_env;
 
+// Default output folder used to store video frames.
+constexpr const base::FilePath::CharType* kDefaultOutputFolder =
+    FILE_PATH_LITERAL("video_frames");
+
 // Video decode test class. Performs setup and teardown for each single test.
 class VideoDecoderTest : public ::testing::Test {
  public:
@@ -70,9 +76,10 @@
     // Write decoded video frames to the 'video_frames/<test_name/>' folder.
     if (g_env->IsFramesOutputEnabled()) {
       base::FilePath output_folder =
-          base::FilePath(FILE_PATH_LITERAL("video_frames"))
+          base::FilePath(g_env->OutputFolder())
               .Append(base::FilePath(g_env->GetTestName()));
       frame_processors.push_back(VideoFrameFileWriter::Create(output_folder));
+      VLOG(0) << "Writing video frames to: " << output_folder;
     }
 
     // Use the new VD-based video decoders if requested.
@@ -301,6 +308,7 @@
   // Parse command line arguments.
   bool enable_validator = true;
   bool output_frames = false;
+  base::FilePath::StringType output_folder = media::test::kDefaultOutputFolder;
   bool use_vd = false;
   base::CommandLine::SwitchMap switches = cmd_line->GetSwitches();
   for (base::CommandLine::SwitchMap::const_iterator it = switches.begin();
@@ -314,6 +322,8 @@
       enable_validator = false;
     } else if (it->first == "output_frames") {
       output_frames = true;
+    } else if (it->first == "output_folder") {
+      output_folder = it->second;
     } else if (it->first == "use_vd") {
       use_vd = true;
     } else {
@@ -329,7 +339,7 @@
   media::test::VideoPlayerTestEnvironment* test_environment =
       media::test::VideoPlayerTestEnvironment::Create(
           video_path, video_metadata_path, enable_validator, output_frames,
-          use_vd);
+          base::FilePath(output_folder), use_vd);
   if (!test_environment)
     return EXIT_FAILURE;
 
diff --git a/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc b/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc
index 1d6cf50d..3c70c07 100644
--- a/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc
+++ b/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc
@@ -7,9 +7,9 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/test/mock_callback.h"
+#include "base/test/scoped_task_environment.h"
 #include "build/build_config.h"
 #include "media/audio/audio_output_delegate.h"
 #include "media/base/audio_parameters.h"
@@ -68,7 +68,7 @@
 }  // namespace
 
 TEST(MojoAudioOutputStreamProviderTest, AcquireTwice_BadMessage) {
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   bool got_bad_message = false;
   mojo::core::SetDefaultProcessErrorCallback(
       base::BindRepeating([](bool* got_bad_message,
@@ -104,7 +104,7 @@
 
 TEST(MojoAudioOutputStreamProviderTest,
      Bitstream_BadMessageOnNonAndoirdPlatforms) {
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   bool got_bad_message = false;
   mojo::core::SetDefaultProcessErrorCallback(
       base::BindRepeating([](bool* got_bad_message,
diff --git a/media/mojo/services/mojo_audio_output_stream_unittest.cc b/media/mojo/services/mojo_audio_output_stream_unittest.cc
index e1d8748..bc447a3 100644
--- a/media/mojo/services/mojo_audio_output_stream_unittest.cc
+++ b/media/mojo/services/mojo_audio_output_stream_unittest.cc
@@ -8,9 +8,9 @@
 
 #include "base/bind.h"
 #include "base/memory/unsafe_shared_memory_region.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/sync_socket.h"
+#include "base/test/scoped_task_environment.h"
 #include "media/audio/audio_output_controller.h"
 #include "media/mojo/interfaces/audio_data_pipe.mojom.h"
 #include "mojo/public/cpp/system/message_pipe.h"
@@ -171,7 +171,7 @@
         .WillOnce(SaveArg<0>(&delegate_event_handler_));
   }
 
-  base::MessageLoop loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   base::CancelableSyncSocket local_;
   std::unique_ptr<TestCancelableSyncSocket> foreign_socket_;
   base::UnsafeSharedMemoryRegion mem_;
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index 59c745e..23c4243 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -832,7 +832,7 @@
 
   // Re-enable audio.
   std::vector<MediaTrack::Id> audio_track_id;
-  audio_track_id.push_back("2");
+  audio_track_id.push_back(MediaTrack::Id("2"));
   OnEnabledAudioTracksChanged(audio_track_id);
 
   // Restart playback from 500ms position.
@@ -896,7 +896,7 @@
   // Disable audio track.
   OnEnabledAudioTracksChanged(track_ids);
   // Re-enable audio track.
-  track_ids.push_back("2");
+  track_ids.push_back(MediaTrack::Id("2"));
   OnEnabledAudioTracksChanged(track_ids);
   // Disable video track.
   OnSelectedVideoTrackChanged(base::nullopt);
@@ -924,7 +924,7 @@
   ASSERT_TRUE(Suspend());
 
   // Re-enable audio track.
-  track_ids.push_back("2");
+  track_ids.push_back(MediaTrack::Id("2"));
   OnEnabledAudioTracksChanged(track_ids);
   ASSERT_TRUE(Resume(TimestampMs(200)));
   ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(300)));
@@ -961,7 +961,7 @@
   ASSERT_TRUE(Resume(TimestampMs(100)));
   // Now re-enable the audio track, playback should continue successfully.
   EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)).Times(1);
-  track_ids.push_back("2");
+  track_ids.push_back(MediaTrack::Id("2"));
   OnEnabledAudioTracksChanged(track_ids);
   ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(200)));
 
@@ -1001,7 +1001,7 @@
 
   // Playback is paused while all audio tracks are disabled.
 
-  track_ids.push_back("2");
+  track_ids.push_back(MediaTrack::Id("2"));
   OnEnabledAudioTracksChanged(track_ids);
   Stop();
 }
@@ -1026,7 +1026,7 @@
   // The first audio track (TrackId=4) is enabled by default. This should
   // disable TrackId=4 and enable TrackId=5.
   std::vector<MediaTrack::Id> track_ids;
-  track_ids.push_back("5");
+  track_ids.push_back(MediaTrack::Id("5"));
   OnEnabledAudioTracksChanged(track_ids);
   ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(200)));
   Stop();
diff --git a/net/test/embedded_test_server/android/embedded_test_server_android.cc b/net/test/embedded_test_server/android/embedded_test_server_android.cc
index 6f6431e..bf505d7 100644
--- a/net/test/embedded_test_server/android/embedded_test_server_android.cc
+++ b/net/test/embedded_test_server/android/embedded_test_server_android.cc
@@ -156,6 +156,10 @@
   base::FilePath test_data_dir(
       base::android::ConvertJavaStringToUTF8(env, jtest_data_dir));
   base::InitAndroidTestPaths(test_data_dir);
+
+  // Bare new does not leak here because the instance deletes itself when it
+  // receives a Destroy() call its Java counterpart. The Java counterpart owns
+  // the instance created here.
   new EmbeddedTestServerAndroid(env, jobj, jhttps);
 }
 
diff --git a/net/test/embedded_test_server/default_handlers.cc b/net/test/embedded_test_server/default_handlers.cc
index 58151d3..68d19bb7 100644
--- a/net/test/embedded_test_server/default_handlers.cc
+++ b/net/test/embedded_test_server/default_handlers.cc
@@ -52,23 +52,23 @@
   if (request.method != METHOD_CONNECT)
     return nullptr;
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_code(HTTP_BAD_REQUEST);
   http_response->set_content(
       "Your client has issued a malformed or illegal request.");
   http_response->set_content_type("text/html");
-  return std::move(http_response);
+  return http_response;
 }
 
 // /cachetime
 // Returns a cacheable response.
 std::unique_ptr<HttpResponse> HandleCacheTime(const HttpRequest& request) {
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_content(
       "<html><head><title>Cache: max-age=60</title></head></html>");
   http_response->set_content_type("text/html");
   http_response->AddCustomHeader("Cache-Control", "max-age=60");
-  return std::move(http_response);
+  return http_response;
 }
 
 // /echoheader?HEADERS | /echoheadercache?HEADERS
@@ -80,7 +80,7 @@
   if (!ShouldHandle(request, url))
     return nullptr;
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
 
   GURL request_url = request.GetURL();
   std::string vary;
@@ -103,14 +103,14 @@
   http_response->set_content(content);
   http_response->set_content_type("text/plain");
   http_response->AddCustomHeader("Cache-Control", cache_control);
-  return std::move(http_response);
+  return http_response;
 }
 
 // /echo?status=STATUS
 // Responds with the request body as the response body and
 // a status code of STATUS.
 std::unique_ptr<HttpResponse> HandleEcho(const HttpRequest& request) {
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
 
   GURL request_url = request.GetURL();
   if (request_url.has_query()) {
@@ -125,17 +125,17 @@
     http_response->set_content("Echo");
   else
     http_response->set_content(request.content);
-  return std::move(http_response);
+  return http_response;
 }
 
 // /echotitle
 // Responds with the request body as the title.
 std::unique_ptr<HttpResponse> HandleEchoTitle(const HttpRequest& request) {
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_content_type("text/html");
   http_response->set_content("<html><head><title>" + request.content +
                              "</title></head></html>");
-  return std::move(http_response);
+  return http_response;
 }
 
 // /echoall?QUERY
@@ -144,7 +144,7 @@
 // Alternative form:
 // /echoall/nocache?QUERY prevents caching of the response.
 std::unique_ptr<HttpResponse> HandleEchoAll(const HttpRequest& request) {
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
 
   std::string body =
       "<html><head><title>EmbeddedTestServer - EchoAll</title><style>"
@@ -177,7 +177,7 @@
                                    "no-cache, no-store, must-revalidate");
   }
 
-  return std::move(http_response);
+  return http_response;
 }
 
 // /echo-raw
@@ -189,7 +189,7 @@
 // /set-cookie?COOKIES
 // Sets response cookies to be COOKIES.
 std::unique_ptr<HttpResponse> HandleSetCookie(const HttpRequest& request) {
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_content_type("text/html");
   std::string content;
   GURL request_url = request.GetURL();
@@ -203,7 +203,7 @@
   }
 
   http_response->set_content(content);
-  return std::move(http_response);
+  return http_response;
 }
 
 // /set-many-cookies?N
@@ -216,7 +216,7 @@
   if (request_url.has_query())
     num = std::atoi(request_url.query().c_str());
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_content_type("text/html");
   for (size_t i = 0; i < num; ++i) {
     http_response->AddCustomHeader("Set-Cookie", "a=");
@@ -224,7 +224,7 @@
 
   http_response->set_content(
       base::StringPrintf("%" PRIuS " cookies were sent", num));
-  return std::move(http_response);
+  return http_response;
 }
 
 // /expect-and-set-cookie?expect=EXPECTED&set=SET&data=DATA
@@ -253,7 +253,7 @@
     }
   }
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_content_type("text/html");
   if (got_all_expected) {
     for (const auto& cookie : query_list.at("set")) {
@@ -270,7 +270,7 @@
   }
 
   http_response->set_content(content);
-  return std::move(http_response);
+  return http_response;
 }
 
 // /set-header?HEADERS
@@ -280,7 +280,7 @@
 
   GURL request_url = request.GetURL();
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_content_type("text/html");
   if (request_url.has_query()) {
     RequestQuery headers = ParseQuery(request_url);
@@ -296,22 +296,21 @@
   }
 
   http_response->set_content(content);
-  return std::move(http_response);
+  return http_response;
 }
 
 // /nocontent
 // Returns a NO_CONTENT response.
 std::unique_ptr<HttpResponse> HandleNoContent(const HttpRequest& request) {
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_code(HTTP_NO_CONTENT);
-  return std::move(http_response);
+  return http_response;
 }
 
 // /close-socket
 // Immediately closes the connection.
 std::unique_ptr<HttpResponse> HandleCloseSocket(const HttpRequest& request) {
-  std::unique_ptr<RawHttpResponse> http_response(new RawHttpResponse("", ""));
-  return std::move(http_response);
+  return std::make_unique<RawHttpResponse>("", "");
 }
 
 // /auth-basic?password=PASS&realm=REALM
@@ -358,7 +357,7 @@
     }
   }
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   if (!authed) {
     http_response->set_code(HTTP_UNAUTHORIZED);
     http_response->set_content_type("text/html");
@@ -372,7 +371,7 @@
         "password: %s<p>You sent:<br>%s<p></body></html>",
         error.c_str(), auth.c_str(), b64str.c_str(), username.c_str(),
         userpass.c_str(), password.c_str(), request.all_headers.c_str()));
-    return std::move(http_response);
+    return http_response;
   }
 
   if (query.find("set-cookie-if-not-challenged") != query.end())
@@ -381,7 +380,7 @@
   if (request.headers.find("If-None-Match") != request.headers.end() &&
       request.headers.at("If-None-Match") == kEtag) {
     http_response->set_code(HTTP_NOT_MODIFIED);
-    return std::move(http_response);
+    return http_response;
   }
 
   base::FilePath file_path =
@@ -405,7 +404,7 @@
 
   http_response->AddCustomHeader("Cache-Control", "max-age=60000");
   http_response->AddCustomHeader("Etag", kEtag);
-  return std::move(http_response);
+  return http_response;
 }
 
 // /auth-digest
@@ -478,7 +477,7 @@
     }
   }
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   if (!authed) {
     http_response->set_code(HTTP_UNAUTHORIZED);
     http_response->set_content_type("text/html");
@@ -494,7 +493,7 @@
         "You sent:<br>%s<p>We are replying:<br>%s<p></body></html>",
         error.c_str(), auth.c_str(), request.all_headers.c_str(),
         auth_header.c_str()));
-    return std::move(http_response);
+    return http_response;
   }
 
   http_response->set_content_type("text/html");
@@ -503,7 +502,7 @@
                          "<body>auth=%s<p></body></html>",
                          username.c_str(), password.c_str(), auth.c_str()));
 
-  return std::move(http_response);
+  return http_response;
 }
 
 // /server-redirect?URL (Also /server-redirect-xxx?URL)
@@ -514,14 +513,14 @@
   std::string dest = UnescapeBinaryURLComponent(request_url.query_piece());
   RequestQuery query = ParseQuery(request_url);
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_code(redirect_code);
   http_response->AddCustomHeader("Location", dest);
   http_response->set_content_type("text/html");
   http_response->set_content(base::StringPrintf(
       "<html><head></head><body>Redirecting to %s</body></html>",
       dest.c_str()));
-  return std::move(http_response);
+  return http_response;
 }
 // /server-redirect-with-cookie?URL
 // Returns a server redirect to URL, and sets the cookie server-redirect=true.
@@ -532,7 +531,7 @@
   std::string dest = UnescapeBinaryURLComponent(request_url.query_piece());
   RequestQuery query = ParseQuery(request_url);
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_code(redirect_code);
   http_response->AddCustomHeader("Location", dest);
   http_response->AddCustomHeader("Set-Cookie", "server-redirect=true");
@@ -540,7 +539,7 @@
   http_response->set_content(base::StringPrintf(
       "<html><head></head><body>Redirecting to %s</body></html>",
       dest.c_str()));
-  return std::move(http_response);
+  return http_response;
 }
 
 // /cross-site?URL
@@ -562,14 +561,14 @@
         dest_all.substr(delimiter + 1).c_str());
   }
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_code(HTTP_MOVED_PERMANENTLY);
   http_response->AddCustomHeader("Location", dest);
   http_response->set_content_type("text/html");
   http_response->set_content(base::StringPrintf(
       "<html><head></head><body>Redirecting to %s</body></html>",
       dest.c_str()));
-  return std::move(http_response);
+  return http_response;
 }
 
 // /client-redirect?URL
@@ -578,24 +577,24 @@
   GURL request_url = request.GetURL();
   std::string dest = UnescapeBinaryURLComponent(request_url.query_piece());
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_content_type("text/html");
   http_response->set_content(base::StringPrintf(
       "<html><head><meta http-equiv=\"refresh\" content=\"0;url=%s\"></head>"
       "<body>Redirecting to %s</body></html>",
       dest.c_str(), dest.c_str()));
-  return std::move(http_response);
+  return http_response;
 }
 
 // /defaultresponse
 // Returns a valid 200 response.
 std::unique_ptr<HttpResponse> HandleDefaultResponse(
     const HttpRequest& request) {
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_content_type("text/html");
   http_response->set_content("Default response given for path: " +
                              request.relative_url);
-  return std::move(http_response);
+  return http_response;
 }
 
 // /slow?N
@@ -607,11 +606,11 @@
   if (request_url.has_query())
     delay = std::atof(request_url.query().c_str());
 
-  std::unique_ptr<BasicHttpResponse> http_response(
-      new DelayedHttpResponse(base::TimeDelta::FromSecondsD(delay)));
+  auto http_response = std::make_unique<DelayedHttpResponse>(
+      base::TimeDelta::FromSecondsD(delay));
   http_response->set_content_type("text/plain");
   http_response->set_content(base::StringPrintf("waited %.1f seconds", delay));
-  return std::move(http_response);
+  return http_response;
 }
 
 // Never returns a response.
@@ -707,11 +706,11 @@
   // CompressGzip should DCHECK itself if this fails, anyways.
   DCHECK_GE(compressed_body.size(), compressed_size);
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_content(
       std::string(compressed_body.data(), compressed_size));
   http_response->AddCustomHeader("Content-Encoding", "gzip");
-  return std::move(http_response);
+  return http_response;
 }
 
 // /self.pac
@@ -725,7 +724,7 @@
       "return 'PROXY %s';\n"
       "}",
       net::HostPortPair::FromURL(request.base_url).ToString().c_str()));
-  return std::move(http_response);
+  return http_response;
 }
 
 }  // anonymous namespace
diff --git a/net/test/embedded_test_server/embedded_test_server.cc b/net/test/embedded_test_server/embedded_test_server.cc
index b836e21..4a3bc753 100644
--- a/net/test/embedded_test_server/embedded_test_server.cc
+++ b/net/test/embedded_test_server/embedded_test_server.cc
@@ -110,7 +110,7 @@
       return false;
     }
 
-    listen_socket_.reset(new TCPServerSocket(nullptr, NetLogSource()));
+    listen_socket_ = std::make_unique<TCPServerSocket>(nullptr, NetLogSource());
 
     int result =
         listen_socket_->ListenWithAddressAndPort("127.0.0.1", port, 10);
@@ -176,7 +176,7 @@
       << "Server must not be started while server is running";
   base::Thread::Options thread_options;
   thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
-  io_thread_.reset(new base::Thread("EmbeddedTestServer IO Thread"));
+  io_thread_ = std::make_unique<base::Thread>("EmbeddedTestServer IO Thread");
   CHECK(io_thread_->StartWithOptions(thread_options));
   CHECK(io_thread_->WaitUntilThreadStarted());
 
@@ -237,8 +237,7 @@
   if (!response) {
     LOG(WARNING) << "Request not handled. Returning 404: "
                  << request->relative_url;
-    std::unique_ptr<BasicHttpResponse> not_found_response(
-        new BasicHttpResponse);
+    auto not_found_response = std::make_unique<BasicHttpResponse>();
     not_found_response->set_code(HTTP_NOT_FOUND);
     response = std::move(not_found_response);
   }
@@ -510,7 +509,7 @@
   // PostTaskAndReply operation if the current thread has no message loop.
   std::unique_ptr<base::MessageLoop> temporary_loop;
   if (!base::MessageLoopCurrent::Get())
-    temporary_loop.reset(new base::MessageLoop());
+    temporary_loop = std::make_unique<base::MessageLoop>();
 
   base::RunLoop run_loop;
   if (!io_thread_->task_runner()->PostTaskAndReply(FROM_HERE, closure,
diff --git a/net/test/embedded_test_server/embedded_test_server.h b/net/test/embedded_test_server/embedded_test_server.h
index e758c06..7978c024 100644
--- a/net/test/embedded_test_server/embedded_test_server.h
+++ b/net/test/embedded_test_server/embedded_test_server.h
@@ -50,7 +50,7 @@
 // The common use case for unit tests is below:
 //
 // void SetUp() {
-//   test_server_.reset(new EmbeddedTestServer());
+//   test_server_ = std::make_unique<EmbeddedTestServer>();
 //   test_server_->RegisterRequestHandler(
 //       base::Bind(&FooTest::HandleRequest, base::Unretained(this)));
 //   ASSERT_TRUE(test_server_.Start());
@@ -61,7 +61,7 @@
 //   if (absolute_url.path() != "/test")
 //     return std::unique_ptr<HttpResponse>();
 //
-//   std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
+//   auto http_response = std::make_unique<BasicHttpResponse>();
 //   http_response->set_code(net::HTTP_OK);
 //   http_response->set_content("hello");
 //   http_response->set_content_type("text/plain");
diff --git a/net/test/embedded_test_server/embedded_test_server_unittest.cc b/net/test/embedded_test_server/embedded_test_server_unittest.cc
index 85155ca1..23a695c9 100644
--- a/net/test/embedded_test_server/embedded_test_server_unittest.cc
+++ b/net/test/embedded_test_server/embedded_test_server_unittest.cc
@@ -133,10 +133,10 @@
     thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
     ASSERT_TRUE(io_thread_.StartWithOptions(thread_options));
 
-    request_context_getter_ =
-        new TestURLRequestContextGetter(io_thread_.task_runner());
+    request_context_getter_ = base::MakeRefCounted<TestURLRequestContextGetter>(
+        io_thread_.task_runner());
 
-    server_.reset(new EmbeddedTestServer(GetParam()));
+    server_ = std::make_unique<EmbeddedTestServer>(GetParam());
     server_->SetConnectionListener(&connection_listener_);
   }
 
@@ -173,11 +173,11 @@
     request_absolute_url_ = request.GetURL();
 
     if (request_absolute_url_.path() == path) {
-      std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+      auto http_response = std::make_unique<BasicHttpResponse>();
       http_response->set_code(code);
       http_response->set_content(content);
       http_response->set_content_type(content_type);
-      return std::move(http_response);
+      return http_response;
     }
 
     return nullptr;
@@ -533,7 +533,7 @@
 
     std::unique_ptr<base::MessageLoop> loop;
     if (message_loop_present_on_initialize_)
-      loop.reset(new base::MessageLoopForIO);
+      loop = std::make_unique<base::MessageLoopForIO>();
 
     // Create the test server instance.
     EmbeddedTestServer server(type_);
@@ -543,13 +543,14 @@
 
     // Make a request and wait for the reply.
     if (!loop)
-      loop.reset(new base::MessageLoopForIO);
+      loop = std::make_unique<base::MessageLoopForIO>();
 
     std::unique_ptr<URLFetcher> fetcher =
         URLFetcher::Create(server.GetURL("/test?q=foo"), URLFetcher::GET, this,
                            TRAFFIC_ANNOTATION_FOR_TESTS);
-    fetcher->SetRequestContext(
-        new TestURLRequestContextGetter(loop->task_runner()));
+    auto test_context_getter =
+        base::MakeRefCounted<TestURLRequestContextGetter>(loop->task_runner());
+    fetcher->SetRequestContext(test_context_getter.get());
     base::RunLoop run_loop;
     quit_run_loop_ = run_loop.QuitClosure();
     fetcher->Start();
diff --git a/net/test/embedded_test_server/http_request.cc b/net/test/embedded_test_server/http_request.cc
index a714157d..7938d874 100644
--- a/net/test/embedded_test_server/http_request.cc
+++ b/net/test/embedded_test_server/http_request.cc
@@ -45,11 +45,10 @@
 }
 
 HttpRequestParser::HttpRequestParser()
-    : http_request_(new HttpRequest()),
+    : http_request_(std::make_unique<HttpRequest>()),
       buffer_position_(0),
       state_(STATE_HEADERS),
-      declared_content_length_(0) {
-}
+      declared_content_length_(0) {}
 
 HttpRequestParser::~HttpRequestParser() = default;
 
@@ -162,7 +161,7 @@
   } else if (http_request_->headers.count("Transfer-Encoding") > 0) {
     if (http_request_->headers["Transfer-Encoding"] == "chunked") {
       http_request_->has_content = true;
-      chunked_decoder_.reset(new HttpChunkedDecoder());
+      chunked_decoder_ = std::make_unique<HttpChunkedDecoder>();
       state_ = STATE_CONTENT;
       return WAITING;
     }
@@ -222,7 +221,7 @@
 
   // Prepare for parsing a new request.
   state_ = STATE_HEADERS;
-  http_request_.reset(new HttpRequest());
+  http_request_ = std::make_unique<HttpRequest>();
   buffer_.clear();
   buffer_position_ = 0;
   declared_content_length_ = 0;
diff --git a/net/test/embedded_test_server/request_handler_util.cc b/net/test/embedded_test_server/request_handler_util.cc
index 314071a..6a28316 100644
--- a/net/test/embedded_test_server/request_handler_util.cc
+++ b/net/test/embedded_test_server/request_handler_util.cc
@@ -161,25 +161,25 @@
 
   RequestQuery query = ParseQuery(request_url);
 
-  std::unique_ptr<BasicHttpResponse> failed_response(new BasicHttpResponse);
+  auto failed_response = std::make_unique<BasicHttpResponse>();
   failed_response->set_code(HTTP_NOT_FOUND);
 
   if (query.find("expected_body") != query.end()) {
     if (request.content.find(query["expected_body"].front()) ==
         std::string::npos) {
-      return std::move(failed_response);
+      return failed_response;
     }
   }
 
   if (query.find("expected_headers") != query.end()) {
     for (const auto& header : query["expected_headers"]) {
       if (header.find(":") == std::string::npos)
-        return std::move(failed_response);
+        return failed_response;
       std::string key = header.substr(0, header.find(":"));
       std::string value = header.substr(header.find(":") + 1);
       if (request.headers.find(key) == request.headers.end() ||
           request.headers.at(key) != value) {
-        return std::move(failed_response);
+        return failed_response;
       }
     }
   }
@@ -199,7 +199,7 @@
     file_contents = "";
 
   if (!UpdateReplacedText(query, &file_contents))
-    return std::move(failed_response);
+    return failed_response;
 
   base::FilePath::StringPieceType mock_headers_extension;
 #if defined(OS_WIN)
@@ -222,7 +222,7 @@
     return std::make_unique<RawHttpResponse>(headers_contents, file_contents);
   }
 
-  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  auto http_response = std::make_unique<BasicHttpResponse>();
   http_response->set_code(HTTP_OK);
 
   if (request.headers.find("Range") != request.headers.end()) {
@@ -248,7 +248,7 @@
   http_response->AddCustomHeader("Accept-Ranges", "bytes");
   http_response->AddCustomHeader("ETag", "'" + file_path.MaybeAsASCII() + "'");
   http_response->set_content(file_contents);
-  return std::move(http_response);
+  return http_response;
 }
 
 }  // namespace test_server
diff --git a/services/device/wake_lock/power_save_blocker/BUILD.gn b/services/device/wake_lock/power_save_blocker/BUILD.gn
index 54a2d248..d8a0b73 100644
--- a/services/device/wake_lock/power_save_blocker/BUILD.gn
+++ b/services/device/wake_lock/power_save_blocker/BUILD.gn
@@ -25,7 +25,7 @@
     "power_save_blocker_android.cc",
     "power_save_blocker_chromeos.cc",
     "power_save_blocker_mac.cc",
-    "power_save_blocker_ozone.cc",
+    "power_save_blocker_stub.cc",
     "power_save_blocker_win.cc",
     "power_save_blocker_x11.cc",
   ]
@@ -53,12 +53,16 @@
     ]
   }
 
-  # Dealing with power_save_blocker_{x11,ozone}.cc is a little complicated
+  # Dealing with power_save_blocker_{x11,stub}.cc is a little complicated
   # given the interaction between os_chromeos and the feature flags for X11 and
   # ozone, so do it all in one spot.
+  #
+  # stub implementation is only used at the moment non-chromeOS Ozone platforms
+  # such as Chromecast.
   if (is_chromeos || !use_ozone) {
-    sources -= [ "power_save_blocker_ozone.cc" ]
+    sources -= [ "power_save_blocker_stub.cc" ]
   }
+
   if (is_chromeos || !use_x11 || !use_dbus) {
     sources -= [ "power_save_blocker_x11.cc" ]
   }
diff --git a/services/device/wake_lock/power_save_blocker/power_save_blocker_ozone.cc b/services/device/wake_lock/power_save_blocker/power_save_blocker_stub.cc
similarity index 79%
rename from services/device/wake_lock/power_save_blocker/power_save_blocker_ozone.cc
rename to services/device/wake_lock/power_save_blocker/power_save_blocker_stub.cc
index 9e728a3b..c2f917a5 100644
--- a/services/device/wake_lock/power_save_blocker/power_save_blocker_ozone.cc
+++ b/services/device/wake_lock/power_save_blocker/power_save_blocker_stub.cc
@@ -9,10 +9,6 @@
 
 namespace device {
 
-// TODO(derat): Consider renaming this file; '_ozone' is a misnomer as power
-// save is OS-specific, not display-system-specific.  This implementation
-// ends up being used for non-ChromeOS Ozone platforms such as Chromecast.
-// See crbug.com/495661 for more detail.
 class PowerSaveBlocker::Delegate
     : public base::RefCountedThreadSafe<PowerSaveBlocker::Delegate> {
  public:
diff --git a/services/identity/public/cpp/DEPS b/services/identity/public/cpp/DEPS
index f5e3e33..ca08848 100644
--- a/services/identity/public/cpp/DEPS
+++ b/services/identity/public/cpp/DEPS
@@ -11,6 +11,7 @@
   "+components/signin/core/browser/oauth2_token_service_delegate_android.h",
   "+components/signin/core/browser/signin_buildflags.h",
   "+components/signin/core/browser/signin_metrics.h",
+  "+components/signin/core/browser/signin_pref_names.h",
   "+components/signin/core/browser/signin_switches.h",
   "+components/signin/core/browser/ubertoken_fetcher_impl.h",
   "+components/signin/core/browser/ubertoken_fetcher.h",
diff --git a/services/identity/public/cpp/identity_test_environment.cc b/services/identity/public/cpp/identity_test_environment.cc
index 0cf120a..104a313 100644
--- a/services/identity/public/cpp/identity_test_environment.cc
+++ b/services/identity/public/cpp/identity_test_environment.cc
@@ -221,7 +221,7 @@
 #if !defined(OS_CHROMEOS)
   primary_account_mutator = std::make_unique<PrimaryAccountMutatorImpl>(
       account_tracker_service.get(),
-      static_cast<SigninManager*>(signin_manager.get()));
+      static_cast<SigninManager*>(signin_manager.get()), pref_service);
 #endif
 
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
diff --git a/services/identity/public/cpp/primary_account_mutator.h b/services/identity/public/cpp/primary_account_mutator.h
index b51a342..0674d7e 100644
--- a/services/identity/public/cpp/primary_account_mutator.h
+++ b/services/identity/public/cpp/primary_account_mutator.h
@@ -56,15 +56,6 @@
       ClearAccountsAction action,
       signin_metrics::ProfileSignout source_metric,
       signin_metrics::SignoutDelete delete_metric) = 0;
-
-  // Getter and setter that allow enabling or disabling the ability to set the
-  // primary account.
-  virtual bool IsSettingPrimaryAccountAllowed() const = 0;
-  virtual void SetSettingPrimaryAccountAllowed(bool allowed) = 0;
-
-  // Sets the pattern controlling which user names are allowed when setting
-  // the primary account.
-  virtual void SetAllowedPrimaryAccountPattern(const std::string& pattern) = 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 a77ea0cd..30f50df 100644
--- a/services/identity/public/cpp/primary_account_mutator_impl.cc
+++ b/services/identity/public/cpp/primary_account_mutator_impl.cc
@@ -6,24 +6,30 @@
 
 #include <utility>
 
+#include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/browser/signin_pref_names.h"
 
 namespace identity {
 
 PrimaryAccountMutatorImpl::PrimaryAccountMutatorImpl(
     AccountTrackerService* account_tracker,
-    SigninManager* signin_manager)
-    : account_tracker_(account_tracker), signin_manager_(signin_manager) {
+    SigninManager* signin_manager,
+    PrefService* pref_service)
+    : account_tracker_(account_tracker),
+      signin_manager_(signin_manager),
+      pref_service_(pref_service) {
   DCHECK(account_tracker_);
   DCHECK(signin_manager_);
+  DCHECK(pref_service_);
 }
 
 PrimaryAccountMutatorImpl::~PrimaryAccountMutatorImpl() {}
 
 bool PrimaryAccountMutatorImpl::SetPrimaryAccount(
     const std::string& account_id) {
-  if (!IsSettingPrimaryAccountAllowed())
+  if (!pref_service_->GetBoolean(prefs::kSigninAllowed))
     return false;
 
   if (signin_manager_->IsAuthenticated())
@@ -62,17 +68,4 @@
   return true;
 }
 
-bool PrimaryAccountMutatorImpl::IsSettingPrimaryAccountAllowed() const {
-  return signin_manager_->IsSigninAllowed();
-}
-
-void PrimaryAccountMutatorImpl::SetSettingPrimaryAccountAllowed(bool allowed) {
-  signin_manager_->SetSigninAllowed(allowed);
-}
-
-void PrimaryAccountMutatorImpl::SetAllowedPrimaryAccountPattern(
-    const std::string& pattern) {
-  NOTIMPLEMENTED();
-}
-
 }  // 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 a75658f..cdef1a9 100644
--- a/services/identity/public/cpp/primary_account_mutator_impl.h
+++ b/services/identity/public/cpp/primary_account_mutator_impl.h
@@ -8,6 +8,7 @@
 #include "services/identity/public/cpp/primary_account_mutator.h"
 
 class AccountTrackerService;
+class PrefService;
 class SigninManager;
 
 namespace identity {
@@ -17,7 +18,8 @@
 class PrimaryAccountMutatorImpl : public PrimaryAccountMutator {
  public:
   PrimaryAccountMutatorImpl(AccountTrackerService* account_tracker,
-                            SigninManager* signin_manager);
+                            SigninManager* signin_manager,
+                            PrefService* pref_service);
   ~PrimaryAccountMutatorImpl() override;
 
   // PrimaryAccountMutator implementation.
@@ -26,15 +28,13 @@
       ClearAccountsAction action,
       signin_metrics::ProfileSignout source_metric,
       signin_metrics::SignoutDelete delete_metric) override;
-  bool IsSettingPrimaryAccountAllowed() const override;
-  void SetSettingPrimaryAccountAllowed(bool allowed) override;
-  void SetAllowedPrimaryAccountPattern(const std::string& pattern) override;
 
  private:
   // Pointers to the services used by the PrimaryAccountMutatorImpl. They
   // *must* outlive this instance.
   AccountTrackerService* account_tracker_ = nullptr;
   SigninManager* signin_manager_ = nullptr;
+  PrefService* pref_service_ = nullptr;
 };
 
 }  // namespace identity
diff --git a/services/identity/public/cpp/primary_account_mutator_unittest.cc b/services/identity/public/cpp/primary_account_mutator_unittest.cc
index cb3fbe0..f6048c7b 100644
--- a/services/identity/public/cpp/primary_account_mutator_unittest.cc
+++ b/services/identity/public/cpp/primary_account_mutator_unittest.cc
@@ -10,6 +10,8 @@
 #include "base/scoped_observer.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/signin/core/browser/signin_metrics.h"
+#include "components/signin/core/browser/signin_pref_names.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
 #include "services/identity/public/cpp/identity_test_utils.h"
 #include "testing/platform_test.h"
@@ -209,28 +211,6 @@
 
 using PrimaryAccountMutatorTest = PlatformTest;
 
-// Checks that the method to control whether setting the primary account is
-// working correctly and that the setting is respected by SetPrimaryAccount().
-TEST_F(PrimaryAccountMutatorTest, SetSettingPrimaryAccountAllowed) {
-  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;
-
-  primary_account_mutator->SetSettingPrimaryAccountAllowed(false);
-  EXPECT_FALSE(primary_account_mutator->IsSettingPrimaryAccountAllowed());
-
-  primary_account_mutator->SetSettingPrimaryAccountAllowed(true);
-  EXPECT_TRUE(primary_account_mutator->IsSettingPrimaryAccountAllowed());
-}
-
 // Checks that setting the primary account works.
 TEST_F(PrimaryAccountMutatorTest, SetPrimaryAccount) {
   base::test::ScopedTaskEnvironment task_environment;
@@ -333,7 +313,10 @@
 TEST_F(PrimaryAccountMutatorTest,
        SetPrimaryAccount_SettingPrimaryAccountForbidden) {
   base::test::ScopedTaskEnvironment task_environment;
-  identity::IdentityTestEnvironment environment;
+
+  sync_preferences::TestingPrefServiceSyncable pref_service;
+  identity::IdentityTestEnvironment environment(
+      /*test_url_loader_factory=*/nullptr, &pref_service);
 
   identity::IdentityManager* identity_manager = environment.identity_manager();
   identity::PrimaryAccountMutator* primary_account_mutator =
@@ -347,8 +330,8 @@
   AccountInfo primary_account_info =
       environment.MakeAccountAvailable(kPrimaryAccountEmail);
 
-  primary_account_mutator->SetSettingPrimaryAccountAllowed(false);
-  EXPECT_FALSE(primary_account_mutator->IsSettingPrimaryAccountAllowed());
+  // Configure prefs so that setting the primary account is disallowed.
+  pref_service.SetBoolean(prefs::kSigninAllowed, false);
 
   EXPECT_FALSE(identity_manager->HasPrimaryAccount());
   EXPECT_FALSE(primary_account_mutator->SetPrimaryAccount(
diff --git a/services/network/origin_policy/origin_policy_constants.h b/services/network/origin_policy/origin_policy_constants.h
index 1fe98db..c9f4600 100644
--- a/services/network/origin_policy/origin_policy_constants.h
+++ b/services/network/origin_policy/origin_policy_constants.h
@@ -10,6 +10,7 @@
 const char kOriginPolicyDeletePolicy[] = "0";
 const char kOriginPolicyReportTo[] = "report-to";
 const char kOriginPolicyPolicy[] = "policy";
+
 // Maximum policy size (implementation-defined limit in bytes).
 // (Limit copied from network::SimpleURLLoader::kMaxBoundedStringDownloadSize.)
 static const size_t kOriginPolicyMaxPolicySize = 1024 * 1024;
diff --git a/services/network/origin_policy/origin_policy_fetcher.cc b/services/network/origin_policy/origin_policy_fetcher.cc
index ea3e03e..d7fba35 100644
--- a/services/network/origin_policy/origin_policy_fetcher.cc
+++ b/services/network/origin_policy/origin_policy_fetcher.cc
@@ -8,11 +8,8 @@
 #include "base/strings/strcat.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_util.h"
-#include "services/network/origin_policy/origin_policy_constants.h"
 #include "services/network/origin_policy/origin_policy_manager.h"
-#include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/cpp/simple_url_loader.h"
-#include "services/network/public/mojom/url_loader_factory.mojom.h"
 
 namespace network {
 
@@ -89,7 +86,6 @@
     std::vector<std::string>* to_be_removed_headers) {
   if (IsValidRedirect(redirect_info)) {
     must_redirect_ = false;
-    // TODO(andypaicu): should we callback with the original url or the new url?
     fetch_url_ = redirect_info.new_url;
     return;
   }
@@ -147,20 +143,17 @@
 
 void OriginPolicyFetcher::WorkDone(std::unique_ptr<std::string> policy_content,
                                    mojom::OriginPolicyState state) {
-  if (callback_) {
-    auto result = mojom::OriginPolicy::New();
-    result->state = state;
-    if (policy_content) {
-      result->contents = mojom::OriginPolicyContents::New();
-      result->contents->raw_policy = *policy_content;
-    }
-    result->policy_url = fetch_url_;
-
-    std::move(callback_).Run(std::move(result));
+  auto result = mojom::OriginPolicy::New();
+  result->state = state;
+  if (policy_content) {
+    result->contents = mojom::OriginPolicyContents::New();
+    result->contents->raw_policy = *policy_content;
   }
+  result->policy_url = fetch_url_;
 
   // Do not add code after this call as it will destroy this object.
-  owner_policy_manager_->FetcherDone(this);
+  owner_policy_manager_->FetcherDone(this, std::move(result),
+                                     std::move(callback_));
 }
 
 bool OriginPolicyFetcher::IsValidRedirect(
diff --git a/services/network/origin_policy/origin_policy_fetcher.h b/services/network/origin_policy/origin_policy_fetcher.h
index 97ac857..87681a08 100644
--- a/services/network/origin_policy/origin_policy_fetcher.h
+++ b/services/network/origin_policy/origin_policy_fetcher.h
@@ -88,7 +88,7 @@
   mojom::OriginPolicyManager::RetrieveOriginPolicyCallback callback_;
 
   // Will be true if we started a fetch at <origin>/well-known/origin-policy
-  // which will redirect to the latest origin policy.
+  // which must redirect to the latest origin policy.
   bool must_redirect_;
 
   DISALLOW_COPY_AND_ASSIGN(OriginPolicyFetcher);
diff --git a/services/network/origin_policy/origin_policy_fetcher_unittest.cc b/services/network/origin_policy/origin_policy_fetcher_unittest.cc
index 042bfc3..8eed79c0 100644
--- a/services/network/origin_policy/origin_policy_fetcher_unittest.cc
+++ b/services/network/origin_policy/origin_policy_fetcher_unittest.cc
@@ -53,6 +53,13 @@
 
     manager_ = std::make_unique<OriginPolicyManager>(
         network_context_->CreateUrlLoaderFactoryForNetworkService());
+
+    test_server_.RegisterRequestHandler(base::BindRepeating(
+        &OriginPolicyFetcherTest::HandleResponse, base::Unretained(this)));
+
+    EXPECT_TRUE(test_server_.Start());
+
+    test_server_origin_ = url::Origin::Create(test_server_.base_url());
   }
 
   const url::Origin& test_server_origin() const { return test_server_origin_; }
@@ -64,16 +71,6 @@
   }
 
  protected:
-  // testing::Test implementation.
-  void SetUp() override {
-    test_server_.RegisterRequestHandler(base::BindRepeating(
-        &OriginPolicyFetcherTest::HandleResponse, base::Unretained(this)));
-
-    EXPECT_TRUE(test_server_.Start());
-
-    test_server_origin_ = url::Origin::Create(test_server_.base_url());
-  }
-
   const net::test_server::EmbeddedTestServer& test_server() const {
     return test_server_;
   }
diff --git a/services/network/origin_policy/origin_policy_manager.cc b/services/network/origin_policy/origin_policy_manager.cc
index ec19690..17f7662 100644
--- a/services/network/origin_policy/origin_policy_manager.cc
+++ b/services/network/origin_policy/origin_policy_manager.cc
@@ -7,9 +7,18 @@
 #include <memory>
 #include <utility>
 
+#include "base/logging.h"
 #include "base/optional.h"
 #include "net/http/http_util.h"
-#include "services/network/origin_policy/origin_policy_constants.h"
+#include "services/network/origin_policy/origin_policy_fetcher.h"
+
+namespace {
+
+// Marker for (temporarily) exempted origins. The presence of the "?" guarantees
+// that this is not a valid policy as it is not a valid http token.
+const char kExemptedOriginPolicyVersion[] = "exception?";
+
+}  // namespace
 
 namespace network {
 
@@ -28,47 +37,82 @@
     const url::Origin& origin,
     const std::string& header_value,
     RetrieveOriginPolicyCallback callback) {
+  DCHECK(origin.GetURL().is_valid());
+  DCHECK(!origin.opaque());
+
   OriginPolicyHeaderValues header_info =
       GetRequestedPolicyAndReportGroupFromHeaderString(header_value);
-  if (header_info.policy_version.empty()) {
-    if (callback) {
-      auto result = mojom::OriginPolicy::New();
-      result->state = mojom::OriginPolicyState::kCannotLoadPolicy;
-      std::move(callback).Run(std::move(result));
-    }
+
+  auto iter = latest_version_map_.find(origin);
+
+  // Process policy deletion first!
+  if (header_info.policy_version == kOriginPolicyDeletePolicy) {
+    if (iter != latest_version_map_.end())
+      latest_version_map_.erase(iter);
+    InvokeCallbackWithPolicyState(origin,
+                                  mojom::OriginPolicyState::kNoPolicyApplies,
+                                  std::move(callback));
     return;
   }
 
-  // Here we might check the cache and only then start the fetch.
-  StartPolicyFetch(origin, header_info, std::move(callback));
+  // Process policy exceptions.
+  if (iter != latest_version_map_.end() &&
+      iter->second == kExemptedOriginPolicyVersion) {
+    InvokeCallbackWithPolicyState(origin,
+                                  mojom::OriginPolicyState::kNoPolicyApplies,
+                                  std::move(callback));
+    return;
+  }
+
+  // No policy applies to this request or invalid header present.
+  if (header_info.policy_version.empty()) {
+    // If there header has no policy version is present, use cached version, if
+    // there is one. Otherwise, fail.
+    if (iter == latest_version_map_.end()) {
+      InvokeCallbackWithPolicyState(
+          origin,
+          header_value.empty() ? mojom::OriginPolicyState::kNoPolicyApplies
+                               : mojom::OriginPolicyState::kCannotLoadPolicy,
+          std::move(callback));
+      return;
+    }
+    header_info.policy_version = iter->second;
+  } else if (iter == latest_version_map_.end()) {
+    latest_version_map_.emplace(origin, header_info.policy_version);
+  } else {
+    iter->second = header_info.policy_version;
+  }
+
+  origin_policy_fetchers_.emplace(std::make_unique<OriginPolicyFetcher>(
+      this, header_info.policy_version, header_info.report_to, origin,
+      url_loader_factory_.get(), std::move(callback)));
+}
+
+void OriginPolicyManager::AddExceptionFor(const url::Origin& origin) {
+  latest_version_map_[origin] = kExemptedOriginPolicyVersion;
+}
+
+void OriginPolicyManager::FetcherDone(OriginPolicyFetcher* fetcher,
+                                      mojom::OriginPolicyPtr origin_policy,
+                                      RetrieveOriginPolicyCallback callback) {
+  std::move(callback).Run(std::move(origin_policy));
+
+  auto it = origin_policy_fetchers_.find(fetcher);
+  DCHECK(it != origin_policy_fetchers_.end());
+  origin_policy_fetchers_.erase(it);
 }
 
 void OriginPolicyManager::RetrieveDefaultOriginPolicy(
     const url::Origin& origin,
     RetrieveOriginPolicyCallback callback) {
-  // Here we might check the cache and only then start the fetch.
-  StartPolicyFetch(origin, OriginPolicyHeaderValues(), std::move(callback));
+  origin_policy_fetchers_.emplace(std::make_unique<OriginPolicyFetcher>(
+      this, std::string() /* report_to */, origin, url_loader_factory_.get(),
+      std::move(callback)));
 }
 
-void OriginPolicyManager::StartPolicyFetch(
-    const url::Origin& origin,
-    const OriginPolicyHeaderValues& header_info,
-    RetrieveOriginPolicyCallback callback) {
-  if (header_info.policy_version.empty()) {
-    origin_policy_fetchers_.emplace(std::make_unique<OriginPolicyFetcher>(
-        this, header_info.report_to, origin, url_loader_factory_.get(),
-        std::move(callback)));
-  } else {
-    origin_policy_fetchers_.emplace(std::make_unique<OriginPolicyFetcher>(
-        this, header_info.policy_version, header_info.report_to, origin,
-        url_loader_factory_.get(), std::move(callback)));
-  }
-}
-
-void OriginPolicyManager::FetcherDone(OriginPolicyFetcher* fetcher) {
-  auto it = origin_policy_fetchers_.find(fetcher);
-  DCHECK(it != origin_policy_fetchers_.end());
-  origin_policy_fetchers_.erase(it);
+// static
+const char* OriginPolicyManager::GetExemptedVersionForTesting() {
+  return kExemptedOriginPolicyVersion;
 }
 
 // static
@@ -102,4 +146,15 @@
   return OriginPolicyHeaderValues({policy.value(), report_to.value_or("")});
 }
 
+// static
+void OriginPolicyManager::InvokeCallbackWithPolicyState(
+    const url::Origin& origin,
+    mojom::OriginPolicyState state,
+    RetrieveOriginPolicyCallback callback) {
+  mojom::OriginPolicyPtr result = mojom::OriginPolicy::New();
+  result->state = state;
+  result->policy_url = OriginPolicyFetcher::GetDefaultPolicyURL(origin);
+  std::move(callback).Run(std::move(result));
+}
+
 }  // namespace network
diff --git a/services/network/origin_policy/origin_policy_manager.h b/services/network/origin_policy/origin_policy_manager.h
index 7b352ce..950d29d1 100644
--- a/services/network/origin_policy/origin_policy_manager.h
+++ b/services/network/origin_policy/origin_policy_manager.h
@@ -5,6 +5,7 @@
 #ifndef SERVICES_NETWORK_ORIGIN_POLICY_ORIGIN_POLICY_MANAGER_H_
 #define SERVICES_NETWORK_ORIGIN_POLICY_ORIGIN_POLICY_MANAGER_H_
 
+#include <map>
 #include <memory>
 #include <set>
 #include <string>
@@ -13,12 +14,14 @@
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/network/origin_policy/origin_policy_fetcher.h"
+#include "services/network/origin_policy/origin_policy_constants.h"
 #include "services/network/public/mojom/origin_policy_manager.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 
 namespace network {
 
+class OriginPolicyFetcher;
+
 // The OriginPolicyManager is the entry point for all Origin Policy related
 // API calls. Spec: https://wicg.github.io/origin-policy/
 // A client will likely call AddBinding (or use the NetworkContext function)
@@ -44,14 +47,17 @@
   // coming through the associated pipe will be served by this object.
   void AddBinding(mojom::OriginPolicyManagerRequest request);
 
-  // mojom::OriginPolicy
+  // mojom::OriginPolicyManager
   void RetrieveOriginPolicy(const url::Origin& origin,
                             const std::string& header_value,
                             RetrieveOriginPolicyCallback callback) override;
+  void AddExceptionFor(const url::Origin& origin) override;
 
   // To be called by fetcher when it has finished its work.
   // This removes the fetcher which results in the fetcher being destroyed.
-  void FetcherDone(OriginPolicyFetcher* fetcher);
+  void FetcherDone(OriginPolicyFetcher* fetcher,
+                   mojom::OriginPolicyPtr origin_policy,
+                   RetrieveOriginPolicyCallback callback);
 
   // Retrieves an origin's default origin policy by attempting to fetch it
   // from "<origin>/.well-known/origin-policy".
@@ -69,17 +75,28 @@
     return GetRequestedPolicyAndReportGroupFromHeaderString(header_value);
   }
 
+  // Get the version used for exempted policies. For testing purposes only.
+  static const char* GetExemptedVersionForTesting();
+
  private:
+  using KnownVersionMap = std::map<url::Origin, std::string>;
+
   // Parses a header and returns the result. If a parsed result does not contain
   // a non-empty policy version it means the `header_value` is invalid.
   static OriginPolicyHeaderValues
   GetRequestedPolicyAndReportGroupFromHeaderString(
       const std::string& header_value);
 
-  // Will start a fetch based on the provided origin and info.
-  void StartPolicyFetch(const url::Origin& origin,
-                        const OriginPolicyHeaderValues& header_info,
-                        RetrieveOriginPolicyCallback callback);
+  // Returns an origin policy with the specified state. The contents is empty
+  // and the `policy_url` is the default policy url for the specified origin.
+  static void InvokeCallbackWithPolicyState(
+      const url::Origin& origin,
+      mojom::OriginPolicyState state,
+      RetrieveOriginPolicyCallback callback);
+
+  // In memory cache of current policy version per origin.
+  // TODO(andypaicu): clear this when the disk cache is cleaned.
+  KnownVersionMap latest_version_map_;
 
   // A list of fetchers owned by this object
   std::set<std::unique_ptr<OriginPolicyFetcher>, base::UniquePtrComparator>
diff --git a/services/network/origin_policy/origin_policy_manager_unittest.cc b/services/network/origin_policy/origin_policy_manager_unittest.cc
index 1a04b27..0848a63 100644
--- a/services/network/origin_policy/origin_policy_manager_unittest.cc
+++ b/services/network/origin_policy/origin_policy_manager_unittest.cc
@@ -5,13 +5,16 @@
 #include <memory>
 #include <utility>
 
+#include "base/strings/strcat.h"
 #include "base/test/scoped_task_environment.h"
 #include "net/http/http_status_code.h"
+#include "net/http/http_util.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
 #include "services/network/network_context.h"
 #include "services/network/network_service.h"
+#include "services/network/origin_policy/origin_policy_fetcher.h"
 #include "services/network/origin_policy/origin_policy_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -41,9 +44,26 @@
         std::move(context_params));
     manager_ = std::make_unique<OriginPolicyManager>(
         network_context_->CreateUrlLoaderFactoryForNetworkService());
+
+    test_server_.RegisterRequestHandler(base::BindRepeating(
+        &OriginPolicyManagerTest::HandleResponse, base::Unretained(this)));
+
+    EXPECT_TRUE(test_server_.Start());
+
+    test_server_origin_ = url::Origin::Create(test_server_.base_url());
+
+    test_server_2_.RegisterRequestHandler(base::BindRepeating(
+        &OriginPolicyManagerTest::HandleResponse, base::Unretained(this)));
+
+    EXPECT_TRUE(test_server_2_.Start());
+
+    test_server_origin_2_ = url::Origin::Create(test_server_2_.base_url());
   }
 
   const url::Origin& test_server_origin() const { return test_server_origin_; }
+  const url::Origin& test_server_origin_2() const {
+    return test_server_origin_2_;
+  }
 
   OriginPolicyManager* manager() { return manager_.get(); }
 
@@ -52,23 +72,17 @@
   void WaitUntilResponseHandled() { response_run_loop.Run(); }
 
  protected:
-  // testing::Test implementation.
-  void SetUp() override {
-    test_server_.RegisterRequestHandler(base::BindRepeating(
-        &OriginPolicyManagerTest::HandleResponse, base::Unretained(this)));
-
-    EXPECT_TRUE(test_server_.Start());
-
-    test_server_origin_ = url::Origin::Create(test_server_.base_url());
-  }
-
   std::unique_ptr<net::test_server::HttpResponse> HandleResponse(
       const net::test_server::HttpRequest& request) {
     response_run_loop.Quit();
     std::unique_ptr<net::test_server::BasicHttpResponse> response =
         std::make_unique<net::test_server::BasicHttpResponse>();
 
-    if (request.relative_url == "/.well-known/origin-policy/policy-1") {
+    if (request.relative_url == "/.well-known/origin-policy") {
+      response->set_code(net::HTTP_FOUND);
+      response->AddCustomHeader("Location",
+                                "/.well-known/origin-policy/policy-1");
+    } else if (request.relative_url == "/.well-known/origin-policy/policy-1") {
       response->set_code(net::HTTP_OK);
       response->set_content("manifest-1");
     } else if (request.relative_url == "/.well-known/origin-policy/policy-2") {
@@ -99,9 +113,11 @@
   std::unique_ptr<NetworkContext> network_context_;
   mojom::NetworkContextPtr network_context_ptr_;
   std::unique_ptr<OriginPolicyManager> manager_;
-  net::test_server::EmbeddedTestServer test_server_;
-  url::Origin test_server_origin_;
   base::RunLoop response_run_loop;
+  net::test_server::EmbeddedTestServer test_server_;
+  net::test_server::EmbeddedTestServer test_server_2_;
+  url::Origin test_server_origin_;
+  url::Origin test_server_origin_2_;
 
   DISALLOW_COPY_AND_ASSIGN(OriginPolicyManagerTest);
 };
@@ -119,8 +135,10 @@
 }
 
 TEST_F(OriginPolicyManagerTest, ParseHeaders) {
+  const std::string kExemptedOriginPolicyVersion =
+      OriginPolicyManager::GetExemptedVersionForTesting();
   const struct {
-    const char* header;
+    const std::string header;
     const char* expected_policy_version;
     const char* expected_report_to;
   } kTests[] = {
@@ -196,6 +214,13 @@
       {"policy=, policy=p2, report-to=r1", "", ""},
       {"policy=, policy=, report-to=r1", "", ""},
       {"policy=p1, report-to=r1, report-to=r2", "", ""},
+
+      // kExemptedOriginPolicyVersion is not a valid version
+      {base::StrCat({"policy=", kExemptedOriginPolicyVersion}), "", ""},
+      {base::StrCat({"report-to=r, policy=", kExemptedOriginPolicyVersion}), "",
+       ""},
+      {base::StrCat({"policy=", kExemptedOriginPolicyVersion, ", report-to=r"}),
+       "", ""},
   };
   for (const auto& test : kTests) {
     SCOPED_TRACE(test.header);
@@ -204,17 +229,21 @@
     EXPECT_EQ(test.expected_policy_version, result.policy_version);
     EXPECT_EQ(test.expected_report_to, result.report_to);
   }
+
+  EXPECT_FALSE(net::HttpUtil::IsToken(kExemptedOriginPolicyVersion));
 }
 
 // Helper class for starting saving a retrieved policy result
 class TestOriginPolicyManagerResult {
  public:
-  TestOriginPolicyManagerResult() {}
+  TestOriginPolicyManagerResult(OriginPolicyManagerTest* fixture,
+                                OriginPolicyManager* manager = nullptr)
+      : fixture_(fixture), manager_(manager ? manager : fixture->manager()) {}
 
   void RetrieveOriginPolicy(const std::string& header_value,
-                            OriginPolicyManagerTest* fixture) {
-    fixture->manager()->RetrieveOriginPolicy(
-        fixture->test_server_origin(), header_value,
+                            const url::Origin* origin = nullptr) {
+    manager_->RetrieveOriginPolicy(
+        origin ? *origin : fixture_->test_server_origin(), header_value,
         base::BindOnce(&TestOriginPolicyManagerResult::Callback,
                        base::Unretained(this)));
     run_loop_.Run();
@@ -231,6 +260,8 @@
   }
 
   base::RunLoop run_loop_;
+  OriginPolicyManagerTest* fixture_;
+  OriginPolicyManager* manager_;
   mojom::OriginPolicyPtr origin_policy_result_;
 
   DISALLOW_COPY_AND_ASSIGN(TestOriginPolicyManagerResult);
@@ -249,7 +280,7 @@
       {"policy=policy-2, report-to=endpoint", mojom::OriginPolicyState::kLoaded,
        "manifest-2"},
 
-      {"", mojom::OriginPolicyState::kCannotLoadPolicy, ""},
+      {"", mojom::OriginPolicyState::kNoPolicyApplies, ""},
       {"unknown=keyword", mojom::OriginPolicyState::kCannotLoadPolicy, ""},
       {"report_to=endpoint", mojom::OriginPolicyState::kCannotLoadPolicy, ""},
       {"policy=policy/policy-3", mojom::OriginPolicyState::kCannotLoadPolicy,
@@ -266,8 +297,11 @@
   for (const auto& test : kTests) {
     SCOPED_TRACE(test.header);
 
-    TestOriginPolicyManagerResult tester;
-    tester.RetrieveOriginPolicy(test.header, this);
+    OriginPolicyManager manager(
+        network_context()->CreateUrlLoaderFactoryForNetworkService());
+
+    TestOriginPolicyManagerResult tester(this, &manager);
+    tester.RetrieveOriginPolicy(test.header);
     EXPECT_EQ(test.expected_state, tester.origin_policy_result()->state);
     if (test.expected_raw_policy.empty()) {
       EXPECT_FALSE(tester.origin_policy_result()->contents);
@@ -302,4 +336,92 @@
   // the test has passed.
 }
 
+TEST_F(OriginPolicyManagerTest, CacheStatesAfterPolicyFetches) {
+  const struct {
+    std::string header;
+    mojom::OriginPolicyState expected_state;
+    std::string expected_raw_policy;
+    const url::Origin& origin;
+  } kTests[] = {
+      // The order of these tests is important as the cache is not cleared in
+      // between tests and some tests rely on the state left over by previous
+      // tests.
+
+      // Nothing in the cache, no policy applies if header unspecified.
+      {"", mojom::OriginPolicyState::kNoPolicyApplies, "",
+       test_server_origin()},
+
+      // An invalid header and nothing in the cache means an error.
+      {"invalid", mojom::OriginPolicyState::kCannotLoadPolicy, "",
+       test_server_origin()},
+
+      // A valid header results in loaded policy.
+      {"policy=policy-1", mojom::OriginPolicyState::kLoaded, "manifest-1",
+       test_server_origin()},
+
+      // With a valid header, we use that version if header unspecified.
+      {"", mojom::OriginPolicyState::kLoaded, "manifest-1",
+       test_server_origin()},
+
+      // A second valid header results in loaded policy. Changes cached last
+      // version.
+      {"policy=policy-2", mojom::OriginPolicyState::kLoaded, "manifest-2",
+       test_server_origin()},
+
+      // The latest version is correctly uses when header is unspecified.
+      {"", mojom::OriginPolicyState::kLoaded, "manifest-2",
+       test_server_origin()},
+
+      // Same as above for invalid header.
+      {"invalid", mojom::OriginPolicyState::kLoaded, "manifest-2",
+       test_server_origin()},
+
+      // Delete the policy.
+      {base::StrCat({"policy=", kOriginPolicyDeletePolicy}),
+       mojom::OriginPolicyState::kNoPolicyApplies, "", test_server_origin()},
+
+      // We are the back to the initial status quo, no policy applies if header
+      // unspecified.
+      {"", mojom::OriginPolicyState::kNoPolicyApplies, "",
+       test_server_origin()},
+
+      // Load a new policy to have something in the cache.
+      {"policy=policy-1", mojom::OriginPolicyState::kLoaded, "manifest-1",
+       test_server_origin()},
+
+      // Check that the version in the cache is used.
+      {"", mojom::OriginPolicyState::kLoaded, "manifest-1",
+       test_server_origin()},
+
+      // In a different origin, it should not pick up the initial origin's
+      // cached version.
+      {"", mojom::OriginPolicyState::kNoPolicyApplies, "",
+       test_server_origin_2()},
+
+      // Load a new policy to have something in the cache for the second origin.
+      {"policy=policy-2", mojom::OriginPolicyState::kLoaded, "manifest-2",
+       test_server_origin_2()},
+
+      // Check that the version in the cache is used for the second origin.
+      {"", mojom::OriginPolicyState::kLoaded, "manifest-2",
+       test_server_origin_2()},
+
+      // The initial origins cached state is unaffected.
+      {"", mojom::OriginPolicyState::kLoaded, "manifest-1",
+       test_server_origin()},
+  };
+
+  for (const auto& test : kTests) {
+    TestOriginPolicyManagerResult tester(this);
+    tester.RetrieveOriginPolicy(test.header, &test.origin);
+    EXPECT_EQ(test.expected_state, tester.origin_policy_result()->state);
+    if (test.expected_raw_policy.empty()) {
+      EXPECT_FALSE(tester.origin_policy_result()->contents);
+    } else {
+      EXPECT_EQ(test.expected_raw_policy,
+                tester.origin_policy_result()->contents->raw_policy);
+    }
+  }
+}
+
 }  // namespace network
diff --git a/services/network/public/mojom/origin_policy_manager.mojom b/services/network/public/mojom/origin_policy_manager.mojom
index 94e8e6a..5785a8f4 100644
--- a/services/network/public/mojom/origin_policy_manager.mojom
+++ b/services/network/public/mojom/origin_policy_manager.mojom
@@ -81,7 +81,14 @@
 interface OriginPolicyManager {
   // Attempts to retrieve the origin policy for an origin and
   // `Sec-Origin-Policy` HTTP header value. Calls back with the result.
+  // The header_value needs to contain a proper policy version or be empty. An
+  // invalid header_value will result in a returned empty policy with the state
+  // of `kCannotLoadPolicy`.
   // https://wicg.github.io/origin-policy/#origin-policy-header
   RetrieveOriginPolicy(url.mojom.Origin origin, string header_value)
       => (OriginPolicy origin_policy);
+
+  // Adds an exception for the specified origin. This means that no policy will
+  // apply for the specified origin from this point forward.
+  AddExceptionFor(url.mojom.Origin origin);
 };
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
index 67d6e02..3d7f0ad 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
@@ -20,6 +20,7 @@
 #include "components/tracing/common/tracing_switches.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/perfetto/include/perfetto/ext/base/utils.h"
 #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
 #include "third_party/perfetto/include/perfetto/protozero/scattered_stream_null_delegate.h"
 #include "third_party/perfetto/include/perfetto/protozero/scattered_stream_writer.h"
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index a2c2d5b..4c6d4127 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -4763,7 +4763,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4778,7 +4779,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4793,7 +4795,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4808,7 +4811,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4823,7 +4827,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4838,7 +4843,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4853,7 +4859,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4871,7 +4878,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4886,7 +4894,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4901,7 +4910,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4916,7 +4926,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4931,7 +4942,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4946,7 +4958,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4961,7 +4974,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4976,7 +4990,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -4991,7 +5006,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5006,7 +5022,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5021,7 +5038,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5039,7 +5057,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5054,7 +5073,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5069,7 +5089,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5087,7 +5108,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5102,7 +5124,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5117,7 +5140,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5135,7 +5159,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5150,7 +5175,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5165,7 +5191,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5180,7 +5207,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5202,7 +5230,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5217,7 +5246,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5232,7 +5262,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5247,7 +5278,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5262,7 +5294,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5277,7 +5310,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5292,7 +5326,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5310,7 +5345,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5325,7 +5361,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5340,7 +5377,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5355,7 +5393,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5370,7 +5409,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5385,7 +5425,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5400,7 +5441,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5415,7 +5457,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5430,7 +5473,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5445,7 +5489,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5460,7 +5505,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5478,7 +5524,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5496,7 +5543,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5511,7 +5559,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5526,7 +5575,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5544,7 +5594,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5559,7 +5610,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5574,7 +5626,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5592,7 +5645,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5607,7 +5661,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5622,7 +5677,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5637,7 +5693,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -5663,7 +5720,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         }
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 179f90e..18808a4 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -1594,7 +1594,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1609,7 +1610,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1624,7 +1626,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1639,7 +1642,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1654,7 +1658,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1669,7 +1674,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1684,7 +1690,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1702,7 +1709,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1717,7 +1725,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1732,7 +1741,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1747,7 +1757,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1762,7 +1773,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1777,7 +1789,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1792,7 +1805,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1807,7 +1821,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1822,7 +1837,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1837,7 +1853,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1852,7 +1869,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1870,7 +1888,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1888,7 +1907,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1903,7 +1923,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1918,7 +1939,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1933,7 +1955,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1948,7 +1971,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1966,7 +1990,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1981,7 +2006,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -1996,7 +2022,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2011,7 +2038,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -2037,7 +2065,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "kvm": "1"
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
             }
           ]
         }
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 9cd98117..56a1898 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1688,6 +1688,9 @@
         'additional_compile_targets': [
           'all',
         ],
+        'mixins': [
+          'linux-xenial',
+        ],
         'swarming': {
           'dimension_sets': [
             {
@@ -1703,6 +1706,9 @@
         'additional_compile_targets': [
           'all',
         ],
+        'mixins': [
+          'linux-xenial',
+        ],
         'swarming': {
           'dimension_sets': [
             {
@@ -3340,6 +3346,9 @@
         'additional_compile_targets': [
           'all',
         ],
+        'mixins': [
+          'linux-xenial',
+        ],
         'swarming': {
           'dimension_sets': [
             {
diff --git a/third_party/blink/common/mediastream/media_stream_mojom_traits.cc b/third_party/blink/common/mediastream/media_stream_mojom_traits.cc
index 8dec0ad2..3ef4409 100644
--- a/third_party/blink/common/mediastream/media_stream_mojom_traits.cc
+++ b/third_party/blink/common/mediastream/media_stream_mojom_traits.cc
@@ -82,119 +82,6 @@
 }
 
 // static
-blink::mojom::MediaStreamRequestResult EnumTraits<
-    blink::mojom::MediaStreamRequestResult,
-    blink::MediaStreamRequestResult>::ToMojom(blink::MediaStreamRequestResult
-                                                  result) {
-  switch (result) {
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_OK:
-      return blink::mojom::MediaStreamRequestResult::OK;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_PERMISSION_DENIED:
-      return blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_PERMISSION_DISMISSED:
-      return blink::mojom::MediaStreamRequestResult::PERMISSION_DISMISSED;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_INVALID_STATE:
-      return blink::mojom::MediaStreamRequestResult::INVALID_STATE;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_NO_HARDWARE:
-      return blink::mojom::MediaStreamRequestResult::NO_HARDWARE;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_INVALID_SECURITY_ORIGIN:
-      return blink::mojom::MediaStreamRequestResult::INVALID_SECURITY_ORIGIN;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_TAB_CAPTURE_FAILURE:
-      return blink::mojom::MediaStreamRequestResult::TAB_CAPTURE_FAILURE;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE:
-      return blink::mojom::MediaStreamRequestResult::SCREEN_CAPTURE_FAILURE;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_CAPTURE_FAILURE:
-      return blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED:
-      return blink::mojom::MediaStreamRequestResult::CONSTRAINT_NOT_SATISFIED;
-    case blink::MediaStreamRequestResult::
-        MEDIA_DEVICE_TRACK_START_FAILURE_AUDIO:
-      return blink::mojom::MediaStreamRequestResult::TRACK_START_FAILURE_AUDIO;
-    case blink::MediaStreamRequestResult::
-        MEDIA_DEVICE_TRACK_START_FAILURE_VIDEO:
-      return blink::mojom::MediaStreamRequestResult::TRACK_START_FAILURE_VIDEO;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_NOT_SUPPORTED:
-      return blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN:
-      return blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_KILL_SWITCH_ON:
-      return blink::mojom::MediaStreamRequestResult::KILL_SWITCH_ON;
-    case blink::MediaStreamRequestResult::MEDIA_DEVICE_SYSTEM_PERMISSION_DENIED:
-      return blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED;
-    default:
-      break;
-  }
-  NOTREACHED();
-  return blink::mojom::MediaStreamRequestResult::OK;
-}
-
-// static
-bool EnumTraits<blink::mojom::MediaStreamRequestResult,
-                blink::MediaStreamRequestResult>::
-    FromMojom(blink::mojom::MediaStreamRequestResult input,
-              blink::MediaStreamRequestResult* out) {
-  switch (input) {
-    case blink::mojom::MediaStreamRequestResult::OK:
-      *out = blink::MediaStreamRequestResult::MEDIA_DEVICE_OK;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED:
-      *out = blink::MediaStreamRequestResult::MEDIA_DEVICE_PERMISSION_DENIED;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::PERMISSION_DISMISSED:
-      *out = blink::MediaStreamRequestResult::MEDIA_DEVICE_PERMISSION_DISMISSED;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::INVALID_STATE:
-      *out = blink::MediaStreamRequestResult::MEDIA_DEVICE_INVALID_STATE;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::NO_HARDWARE:
-      *out = blink::MediaStreamRequestResult::MEDIA_DEVICE_NO_HARDWARE;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::INVALID_SECURITY_ORIGIN:
-      *out =
-          blink::MediaStreamRequestResult::MEDIA_DEVICE_INVALID_SECURITY_ORIGIN;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::TAB_CAPTURE_FAILURE:
-      *out = blink::MediaStreamRequestResult::MEDIA_DEVICE_TAB_CAPTURE_FAILURE;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::SCREEN_CAPTURE_FAILURE:
-      *out =
-          blink::MediaStreamRequestResult::MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE:
-      *out = blink::MediaStreamRequestResult::MEDIA_DEVICE_CAPTURE_FAILURE;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::CONSTRAINT_NOT_SATISFIED:
-      *out = blink::MediaStreamRequestResult::
-          MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::TRACK_START_FAILURE_AUDIO:
-      *out = blink::MediaStreamRequestResult::
-          MEDIA_DEVICE_TRACK_START_FAILURE_AUDIO;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::TRACK_START_FAILURE_VIDEO:
-      *out = blink::MediaStreamRequestResult::
-          MEDIA_DEVICE_TRACK_START_FAILURE_VIDEO;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED:
-      *out = blink::MediaStreamRequestResult::MEDIA_DEVICE_NOT_SUPPORTED;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN:
-      *out =
-          blink::MediaStreamRequestResult::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::KILL_SWITCH_ON:
-      *out = blink::MediaStreamRequestResult::MEDIA_DEVICE_KILL_SWITCH_ON;
-      return true;
-    case blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED:
-      *out = blink::MediaStreamRequestResult::
-          MEDIA_DEVICE_SYSTEM_PERMISSION_DENIED;
-      return true;
-  }
-  NOTREACHED();
-  return false;
-}
-
-// static
 bool StructTraits<blink::mojom::MediaStreamDeviceDataView,
                   blink::MediaStreamDevice>::
     Read(blink::mojom::MediaStreamDeviceDataView input,
diff --git a/third_party/blink/public/common/mediastream/media_stream.typemap b/third_party/blink/public/common/mediastream/media_stream.typemap
index f18a24d..2fded1c0 100644
--- a/third_party/blink/public/common/mediastream/media_stream.typemap
+++ b/third_party/blink/public/common/mediastream/media_stream.typemap
@@ -16,7 +16,6 @@
 
 type_mappings = [
   "blink.mojom.MediaStreamDevice=blink::MediaStreamDevice",
-  "blink.mojom.MediaStreamRequestResult=blink::MediaStreamRequestResult",
   "blink.mojom.MediaStreamType=blink::MediaStreamType",
   "blink.mojom.StreamControls=blink::StreamControls",
   "blink.mojom.TrackControls=blink::TrackControls",
diff --git a/third_party/blink/public/common/mediastream/media_stream_mojom_traits.h b/third_party/blink/public/common/mediastream/media_stream_mojom_traits.h
index a3c3fca..6815314 100644
--- a/third_party/blink/public/common/mediastream/media_stream_mojom_traits.h
+++ b/third_party/blink/public/common/mediastream/media_stream_mojom_traits.h
@@ -22,16 +22,6 @@
 };
 
 template <>
-struct BLINK_COMMON_EXPORT EnumTraits<blink::mojom::MediaStreamRequestResult,
-                                      blink::MediaStreamRequestResult> {
-  static blink::mojom::MediaStreamRequestResult ToMojom(
-      blink::MediaStreamRequestResult result);
-
-  static bool FromMojom(blink::mojom::MediaStreamRequestResult input,
-                        blink::MediaStreamRequestResult* out);
-};
-
-template <>
 struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::MediaStreamDeviceDataView,
                                         blink::MediaStreamDevice> {
   static const blink::MediaStreamType& type(
diff --git a/third_party/blink/public/common/mediastream/media_stream_request.h b/third_party/blink/public/common/mediastream/media_stream_request.h
index c062c41..117db7b 100644
--- a/third_party/blink/public/common/mediastream/media_stream_request.h
+++ b/third_party/blink/public/common/mediastream/media_stream_request.h
@@ -57,28 +57,6 @@
   MEDIA_OPEN_DEVICE_PEPPER_ONLY  // Only used in requests made by Pepper.
 };
 
-// Elements in this enum should not be deleted or rearranged; the only
-// permitted operation is to add new elements before NUM_MEDIA_REQUEST_RESULTS.
-enum MediaStreamRequestResult {
-  MEDIA_DEVICE_OK = 0,
-  MEDIA_DEVICE_PERMISSION_DENIED = 1,
-  MEDIA_DEVICE_PERMISSION_DISMISSED = 2,
-  MEDIA_DEVICE_INVALID_STATE = 3,
-  MEDIA_DEVICE_NO_HARDWARE = 4,
-  MEDIA_DEVICE_INVALID_SECURITY_ORIGIN = 5,
-  MEDIA_DEVICE_TAB_CAPTURE_FAILURE = 6,
-  MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE = 7,
-  MEDIA_DEVICE_CAPTURE_FAILURE = 8,
-  MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED = 9,
-  MEDIA_DEVICE_TRACK_START_FAILURE_AUDIO = 10,
-  MEDIA_DEVICE_TRACK_START_FAILURE_VIDEO = 11,
-  MEDIA_DEVICE_NOT_SUPPORTED = 12,
-  MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN = 13,
-  MEDIA_DEVICE_KILL_SWITCH_ON = 14,
-  MEDIA_DEVICE_SYSTEM_PERMISSION_DENIED = 15,
-  NUM_MEDIA_REQUEST_RESULTS
-};
-
 // Convenience predicates to determine whether the given type represents some
 // audio or some video device.
 BLINK_COMMON_EXPORT bool IsAudioInputMediaType(MediaStreamType type);
diff --git a/third_party/blink/public/mojom/mediastream/media_stream.mojom b/third_party/blink/public/mojom/mediastream/media_stream.mojom
index c4356b1..a3b25fd 100644
--- a/third_party/blink/public/mojom/mediastream/media_stream.mojom
+++ b/third_party/blink/public/mojom/mediastream/media_stream.mojom
@@ -22,7 +22,8 @@
   NUM_MEDIA_TYPES
 };
 
-// See public/common/media_stream_request.h.
+// Elements in this enum should not be deleted or rearranged; the only
+// permitted operation is to add new elements before NUM_MEDIA_REQUEST_RESULTS.
 enum MediaStreamRequestResult {
   OK,
   PERMISSION_DENIED,
@@ -39,7 +40,8 @@
   NOT_SUPPORTED,
   FAILED_DUE_TO_SHUTDOWN,
   KILL_SWITCH_ON,
-  SYSTEM_PERMISSION_DENIED
+  SYSTEM_PERMISSION_DENIED,
+  NUM_MEDIA_REQUEST_RESULTS
 };
 
 // See public/common/media_stream_request.h.
diff --git a/third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h b/third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h
index 53ef3e5..8953e863 100644
--- a/third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h
+++ b/third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h
@@ -7,6 +7,7 @@
 
 #include "base/callback.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
 #include "third_party/blink/public/platform/web_private_ptr.h"
@@ -32,15 +33,15 @@
 
   using ConstraintsCallback =
       base::Callback<void(WebPlatformMediaStreamSource* source,
-                          MediaStreamRequestResult result,
+                          mojom::MediaStreamRequestResult result,
                           const WebString& result_name)>;
   using ConstraintsRepeatingCallback =
       base::RepeatingCallback<void(WebPlatformMediaStreamSource* source,
-                                   MediaStreamRequestResult result,
+                                   mojom::MediaStreamRequestResult result,
                                    const WebString& result_name)>;
   using ConstraintsOnceCallback =
       base::OnceCallback<void(WebPlatformMediaStreamSource* source,
-                              MediaStreamRequestResult result,
+                              mojom::MediaStreamRequestResult result,
                               const WebString& result_name)>;
 
   // Source constraints key for
diff --git a/third_party/blink/public/platform/modules/mediastream/webrtc_uma_histograms.h b/third_party/blink/public/platform/modules/mediastream/webrtc_uma_histograms.h
index 7658c4c..dd6dcbb3 100644
--- a/third_party/blink/public/platform/modules/mediastream/webrtc_uma_histograms.h
+++ b/third_party/blink/public/platform/modules/mediastream/webrtc_uma_histograms.h
@@ -9,6 +9,7 @@
 #include "base/memory/singleton.h"
 #include "base/sequence_checker.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_rtc_api_name.h"
 
@@ -31,7 +32,7 @@
 BLINK_PLATFORM_EXPORT void LogUserMediaRequestWithNoResult(
     MediaStreamRequestState state);
 BLINK_PLATFORM_EXPORT void LogUserMediaRequestResult(
-    MediaStreamRequestResult result);
+    mojom::MediaStreamRequestResult result);
 
 // Helper method used to collect information about the number of times
 // different WebRTC APIs are called from JavaScript.
diff --git a/third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h b/third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h
index 6db8689..b9b9cc8 100644
--- a/third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h
+++ b/third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h
@@ -5,12 +5,19 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_DEDICATED_WORKER_HOST_FACTORY_CLIENT_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_DEDICATED_WORKER_HOST_FACTORY_CLIENT_H_
 
+#include "base/memory/ref_counted.h"
 #include "mojo/public/cpp/system/message_pipe.h"
-#include "third_party/blink/public/platform/web_security_origin.h"
-#include "third_party/blink/public/platform/web_url.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
 
 namespace blink {
 
+class WebSecurityOrigin;
+class WebURL;
+class WebWorkerFetchContext;
+
 // PlzDedicatedWorker:
 // WebDedicatedWorkerHostFactoryClient is the interface to access
 // content::DedicatedWorkerHostFactoryClient from blink::DedicatedWorker.
@@ -28,6 +35,11 @@
       const blink::WebURL& script_url,
       const blink::WebSecurityOrigin& script_origin,
       mojo::ScopedMessagePipeHandle blob_url_token) = 0;
+
+  // Clones the given WebWorkerFetchContext for nested workers.
+  virtual scoped_refptr<WebWorkerFetchContext> CloneWorkerFetchContext(
+      WebWorkerFetchContext*,
+      scoped_refptr<base::SingleThreadTaskRunner>) = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/platform/web_worker_fetch_context.h b/third_party/blink/public/platform/web_worker_fetch_context.h
index 3f7b1cb..f67d193 100644
--- a/third_party/blink/public/platform/web_worker_fetch_context.h
+++ b/third_party/blink/public/platform/web_worker_fetch_context.h
@@ -56,12 +56,6 @@
 
   virtual ~WebWorkerFetchContext() = default;
 
-  // Used to copy a worker fetch context between worker threads.
-  virtual scoped_refptr<WebWorkerFetchContext> CloneForNestedWorker(
-      scoped_refptr<base::SingleThreadTaskRunner>) {
-    return nullptr;
-  }
-
   // Set a raw pointer of a WaitableEvent which will be signaled from the main
   // thread when the worker's GlobalScope is terminated, which will terminate
   // sync loading requests on the worker thread. It is guaranteed that the
diff --git a/third_party/blink/public/web/modules/mediastream/media_stream_video_source.h b/third_party/blink/public/web/modules/mediastream/media_stream_video_source.h
index b7c6b339..ec6612a 100644
--- a/third_party/blink/public/web/modules/mediastream/media_stream_video_source.h
+++ b/third_party/blink/public/web/modules/mediastream/media_stream_video_source.h
@@ -17,6 +17,7 @@
 #include "media/base/video_frame.h"
 #include "media/capture/video_capture_types.h"
 #include "third_party/blink/public/common/media/video_capture.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_types.h"
 #include "third_party/blink/public/platform/modules/mediastream/secure_display_link_tracker.h"
 #include "third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h"
@@ -178,7 +179,7 @@
   // captured frames.
   virtual void StartSourceImpl(
       const VideoCaptureDeliverFrameCB& frame_callback) = 0;
-  void OnStartDone(MediaStreamRequestResult result);
+  void OnStartDone(mojom::MediaStreamRequestResult result);
 
   // A subclass that supports restart must override this method such that it
   // immediately stop producing video frames after this method is called.
diff --git a/third_party/blink/renderer/DEPS b/third_party/blink/renderer/DEPS
index 5ba4fc2..0f1550d 100644
--- a/third_party/blink/renderer/DEPS
+++ b/third_party/blink/renderer/DEPS
@@ -5,6 +5,7 @@
     "+base/bit_cast.h",
     "+base/callback.h",
     "+base/callback_forward.h",
+    "+base/containers/adapters.h",
     "+base/containers/span.h",
     "+base/debug",
     "+base/feature_list.h",
diff --git a/third_party/blink/renderer/core/accessibility/apply_dark_mode_test.cc b/third_party/blink/renderer/core/accessibility/apply_dark_mode_test.cc
index bb597e7..4749ee01 100644
--- a/third_party/blink/renderer/core/accessibility/apply_dark_mode_test.cc
+++ b/third_party/blink/renderer/core/accessibility/apply_dark_mode_test.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/html/html_head_element.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 namespace {
@@ -60,8 +61,8 @@
 }
 
 TEST_F(ApplyDarkModeCheckTest, MetaColorSchemeDark) {
-  RuntimeEnabledFeatures::SetCSSColorSchemeEnabled(true);
-  RuntimeEnabledFeatures::SetMetaColorSchemeEnabled(true);
+  ScopedCSSColorSchemeForTest css_feature_scope(true);
+  ScopedMetaColorSchemeForTest meta_feature_scope(true);
   GetDocument().GetSettings()->SetForceDarkModeEnabled(true);
   GetDocument().GetSettings()->SetPreferredColorScheme(
       PreferredColorScheme::kDark);
diff --git a/third_party/blink/renderer/core/animation/animation_timeline.idl b/third_party/blink/renderer/core/animation/animation_timeline.idl
index 4d4d6ea..cf19ed3f 100644
--- a/third_party/blink/renderer/core/animation/animation_timeline.idl
+++ b/third_party/blink/renderer/core/animation/animation_timeline.idl
@@ -5,7 +5,8 @@
 // https://drafts.csswg.org/web-animations/#the-animationtimeline-interface
 
 [
-    RuntimeEnabled=WebAnimationsAPI
+    RuntimeEnabled=WebAnimationsAPI,
+    Exposed=Window
 ] interface AnimationTimeline {
     readonly attribute double? currentTime;
 };
diff --git a/third_party/blink/renderer/core/animation/document_timeline.idl b/third_party/blink/renderer/core/animation/document_timeline.idl
index 8a5aed4..4244b5a 100644
--- a/third_party/blink/renderer/core/animation/document_timeline.idl
+++ b/third_party/blink/renderer/core/animation/document_timeline.idl
@@ -7,6 +7,7 @@
 [
     Constructor(optional DocumentTimelineOptions options),
     ConstructorCallWith=ExecutionContext,
-    RuntimeEnabled=WebAnimationsAPI
+    RuntimeEnabled=WebAnimationsAPI,
+    Exposed=Window
 ] interface DocumentTimeline : AnimationTimeline {
 };
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.idl b/third_party/blink/renderer/core/animation/scroll_timeline.idl
index 329f260f..96520d2 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline.idl
+++ b/third_party/blink/renderer/core/animation/scroll_timeline.idl
@@ -9,7 +9,8 @@
     ConstructorCallWith=Document,
     MeasureAs=ScrollTimelineConstructor,
     RaisesException=Constructor,
-    RuntimeEnabled=AnimationWorklet
+    RuntimeEnabled=AnimationWorklet,
+    Exposed=Window
 ] interface ScrollTimeline : AnimationTimeline {
     readonly attribute Element? scrollSource;
     readonly attribute ScrollDirection orientation;
diff --git a/third_party/blink/renderer/core/css/css_condition_rule.idl b/third_party/blink/renderer/core/css/css_condition_rule.idl
index 824a340d..a2d8b3ef 100644
--- a/third_party/blink/renderer/core/css/css_condition_rule.idl
+++ b/third_party/blink/renderer/core/css/css_condition_rule.idl
@@ -4,7 +4,9 @@
 
 // https://drafts.csswg.org/css-conditional/#the-cssconditionrule-interface
 
-interface CSSConditionRule : CSSGroupingRule {
+[
+    Exposed=Window
+] interface CSSConditionRule : CSSGroupingRule {
     // TODO(xing.xu): readonly should be removed.
     readonly attribute DOMString conditionText;
 };
diff --git a/third_party/blink/renderer/core/css/css_font_feature_values_rule.idl b/third_party/blink/renderer/core/css/css_font_feature_values_rule.idl
index e65dd40..882239c 100644
--- a/third_party/blink/renderer/core/css/css_font_feature_values_rule.idl
+++ b/third_party/blink/renderer/core/css/css_font_feature_values_rule.idl
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// https://drafts.csswg.org/css-fonts-4/#om-fontfeaturevalues
+
 [
     RuntimeEnabled=CSSFontFeatureValues
 ] interface CSSFontFeatureValuesRule : CSSRule {
diff --git a/third_party/blink/renderer/core/css/css_keyframe_rule.idl b/third_party/blink/renderer/core/css/css_keyframe_rule.idl
index 07cbc1c..59b9325 100644
--- a/third_party/blink/renderer/core/css/css_keyframe_rule.idl
+++ b/third_party/blink/renderer/core/css/css_keyframe_rule.idl
@@ -28,7 +28,9 @@
 
 // https://drafts.csswg.org/css-animations/#interface-csskeyframerule
 
-interface CSSKeyframeRule : CSSRule {
+[
+    Exposed=Window
+] interface CSSKeyframeRule : CSSRule {
     [RaisesException=Setter] attribute DOMString keyText;
     [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style;
 };
diff --git a/third_party/blink/renderer/core/css/css_keyframes_rule.idl b/third_party/blink/renderer/core/css/css_keyframes_rule.idl
index 01e48fd9..17e8616 100644
--- a/third_party/blink/renderer/core/css/css_keyframes_rule.idl
+++ b/third_party/blink/renderer/core/css/css_keyframes_rule.idl
@@ -28,7 +28,9 @@
 
 // https://drafts.csswg.org/css-animations/#interface-csskeyframesrule
 
-interface CSSKeyframesRule : CSSRule {
+[
+    Exposed=Window
+] interface CSSKeyframesRule : CSSRule {
     attribute DOMString name;
     readonly attribute CSSRuleList cssRules;
 
diff --git a/third_party/blink/renderer/core/css/css_supports_rule.idl b/third_party/blink/renderer/core/css/css_supports_rule.idl
index 705a387d..2cfb265 100644
--- a/third_party/blink/renderer/core/css/css_supports_rule.idl
+++ b/third_party/blink/renderer/core/css/css_supports_rule.idl
@@ -28,5 +28,7 @@
 
 // https://drafts.csswg.org/css-conditional/#the-csssupportsrule-interface
 
-interface CSSSupportsRule : CSSConditionRule {
+[
+    Exposed=Window
+] interface CSSSupportsRule : CSSConditionRule {
 };
diff --git a/third_party/blink/renderer/core/css/css_viewport_rule.idl b/third_party/blink/renderer/core/css/css_viewport_rule.idl
index 4c1894b..26d84823 100644
--- a/third_party/blink/renderer/core/css/css_viewport_rule.idl
+++ b/third_party/blink/renderer/core/css/css_viewport_rule.idl
@@ -30,7 +30,8 @@
 // https://drafts.csswg.org/css-device-adapt/#css-viewport-rule-interface
 
 [
-    RuntimeEnabled=CSSViewport
+    RuntimeEnabled=CSSViewport,
+    Exposed=Window
 ] interface CSSViewportRule : CSSRule {
     readonly attribute CSSStyleDeclaration style;
 };
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
index 98538609..78195b2 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
@@ -328,7 +328,7 @@
 }
 
 TEST(CSSPropertyParserTest, ScrollCustomizationPropertySingleValue) {
-  RuntimeEnabledFeatures::SetScrollCustomizationEnabled(true);
+  ScopedScrollCustomizationForTest scoped_feature(true);
   const CSSValue* value = CSSParser::ParseSingleValue(
       CSSPropertyID::kScrollCustomization, "pan-down",
       StrictCSSParserContext(SecureContextMode::kSecureContext));
@@ -339,7 +339,7 @@
 }
 
 TEST(CSSPropertyParserTest, ScrollCustomizationPropertyTwoValuesCombined) {
-  RuntimeEnabledFeatures::SetScrollCustomizationEnabled(true);
+  ScopedScrollCustomizationForTest scoped_feature(true);
   const CSSValue* value = CSSParser::ParseSingleValue(
       CSSPropertyID::kScrollCustomization, "pan-left pan-y",
       StrictCSSParserContext(SecureContextMode::kSecureContext));
@@ -353,7 +353,7 @@
 
 TEST(CSSPropertyParserTest, ScrollCustomizationPropertyInvalidEntries) {
   // We expect exactly one property value per coordinate.
-  RuntimeEnabledFeatures::SetScrollCustomizationEnabled(true);
+  ScopedScrollCustomizationForTest scoped_feature(true);
   const CSSValue* value = CSSParser::ParseSingleValue(
       CSSPropertyID::kScrollCustomization, "pan-left pan-right",
       StrictCSSParserContext(SecureContextMode::kSecureContext));
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 3b9bcc9..6b640c1b 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -1924,7 +1924,7 @@
     CSSParserTokenRange& range,
     const CSSParserContext& context,
     const CSSParserLocalContext&) const {
-  if (range.Peek().Id() == CSSValueID::kAuto)
+  if (range.Peek().Id() == CSSValueID::kNormal)
     return css_property_parser_helpers::ConsumeIdent(range);
   if (range.Peek().Id() == CSSValueID::kOnly) {
     // Handle 'only light'
@@ -1939,10 +1939,10 @@
   CSSValueList* values = CSSValueList::CreateSpaceSeparated();
   do {
     CSSValueID id = range.Peek().Id();
-    // 'auto' is handled above, and 'none' is reserved for future use. 'revert'
-    // is not yet implemented as a keyword, but still skip it for compat and
-    // interop.
-    if (id == CSSValueID::kAuto || id == CSSValueID::kNone ||
+    // 'normal' is handled above, and 'none' is reserved for future use.
+    // 'revert' is not yet implemented as a keyword, but still skip it for
+    // compat and interop.
+    if (id == CSSValueID::kNormal || id == CSSValueID::kNone ||
         id == CSSValueID::kRevert || id == CSSValueID::kDefault) {
       return nullptr;
     }
@@ -1975,7 +1975,7 @@
     Node*,
     bool allow_visited_style) const {
   if (style.ColorScheme().IsEmpty())
-    return CSSIdentifierValue::Create(CSSValueID::kAuto);
+    return CSSIdentifierValue::Create(CSSValueID::kNormal);
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   for (auto ident : style.ColorScheme()) {
     list->Append(*MakeGarbageCollected<CSSCustomIdentValue>(ident));
@@ -1984,7 +1984,7 @@
 }
 
 const CSSValue* ColorScheme::InitialValue() const {
-  return CSSIdentifierValue::Create(CSSValueID::kAuto);
+  return CSSIdentifierValue::Create(CSSValueID::kNormal);
 }
 
 void ColorScheme::ApplyInitial(StyleResolverState& state) const {
@@ -2000,7 +2000,7 @@
 void ColorScheme::ApplyValue(StyleResolverState& state,
                              const CSSValue& value) const {
   if (const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
-    DCHECK(identifier_value->GetValueID() == CSSValueID::kAuto);
+    DCHECK(identifier_value->GetValueID() == CSSValueID::kNormal);
     state.Style()->SetColorScheme(Vector<AtomicString>());
     state.Style()->SetDarkColorScheme(false);
   } else if (const auto* scheme_list = DynamicTo<CSSValueList>(value)) {
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 f09c6ceb..dbed68e 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -35,6 +35,7 @@
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/geometry/float_size.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 
@@ -43,7 +44,6 @@
 class StyleEngineTest : public testing::Test {
  protected:
   void SetUp() override;
-  void TearDown() override;
 
   Document& GetDocument() { return dummy_page_holder_->GetDocument(); }
   StyleEngine& GetStyleEngine() { return GetDocument().GetStyleEngine(); }
@@ -67,17 +67,12 @@
 
  private:
   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
-  RuntimeEnabledFeatures::Backup features_backup_;
 };
 
 void StyleEngineTest::SetUp() {
   dummy_page_holder_ = std::make_unique<DummyPageHolder>(IntSize(800, 600));
 }
 
-void StyleEngineTest::TearDown() {
-  features_backup_.Restore();
-}
-
 StyleEngineTest::RuleSetInvalidation
 StyleEngineTest::ScheduleInvalidationsForRules(TreeScope& tree_scope,
                                                const String& css_text) {
@@ -1490,7 +1485,7 @@
 }
 
 TEST_F(StyleEngineTest, MediaQueriesChangeColorScheme) {
-  RuntimeEnabledFeatures::SetMediaQueryPrefersColorSchemeEnabled(true);
+  ScopedMediaQueryPrefersColorSchemeForTest feature_scope(true);
 
   GetDocument().body()->SetInnerHTMLFromString(R"HTML(
     <style>
@@ -1516,7 +1511,7 @@
 }
 
 TEST_F(StyleEngineTest, MediaQueriesChangeColorSchemeForcedDarkMode) {
-  RuntimeEnabledFeatures::SetMediaQueryPrefersColorSchemeEnabled(true);
+  ScopedMediaQueryPrefersColorSchemeForTest feature_scope(true);
 
   GetDocument().GetSettings()->SetForceDarkModeEnabled(true);
   GetDocument().GetSettings()->SetPreferredColorScheme(
diff --git a/third_party/blink/renderer/core/dom/attr.idl b/third_party/blink/renderer/core/dom/attr.idl
index 0d099d80..27c31ce8 100644
--- a/third_party/blink/renderer/core/dom/attr.idl
+++ b/third_party/blink/renderer/core/dom/attr.idl
@@ -19,8 +19,9 @@
  */
 
 // https://dom.spec.whatwg.org/#interface-attr
-
-interface Attr : Node {
+[
+    Exposed=Window
+] interface Attr : Node {
     readonly attribute DOMString? namespaceURI;
     readonly attribute DOMString? prefix;
     readonly attribute DOMString localName;
diff --git a/third_party/blink/renderer/core/dom/cdata_section.idl b/third_party/blink/renderer/core/dom/cdata_section.idl
index 6cd8f2270..aa09f99 100644
--- a/third_party/blink/renderer/core/dom/cdata_section.idl
+++ b/third_party/blink/renderer/core/dom/cdata_section.idl
@@ -19,5 +19,7 @@
 
 // https://dom.spec.whatwg.org/#interface-cdatasection
 
-interface CDATASection : Text {
+[
+    Exposed=Window
+] interface CDATASection : Text {
 };
diff --git a/third_party/blink/renderer/core/dom/character_data.idl b/third_party/blink/renderer/core/dom/character_data.idl
index 40df5dd..3b1a441b 100644
--- a/third_party/blink/renderer/core/dom/character_data.idl
+++ b/third_party/blink/renderer/core/dom/character_data.idl
@@ -18,8 +18,9 @@
  */
 
 // https://dom.spec.whatwg.org/#interface-characterdata
-
-interface CharacterData : Node {
+[
+  Exposed=Window
+] interface CharacterData : Node {
     attribute [TreatNullAs=EmptyString] DOMString data;
     readonly attribute unsigned long length;
     [RaisesException] DOMString substringData(unsigned long offset, unsigned long count);
diff --git a/third_party/blink/renderer/core/dom/comment.idl b/third_party/blink/renderer/core/dom/comment.idl
index c22cc4a..06e5126 100644
--- a/third_party/blink/renderer/core/dom/comment.idl
+++ b/third_party/blink/renderer/core/dom/comment.idl
@@ -21,6 +21,7 @@
 
 [
     Constructor(optional DOMString data = ""),
-    ConstructorCallWith=Document
+    ConstructorCallWith=Document,
+    Exposed=Window
 ] interface Comment : CharacterData {
 };
diff --git a/third_party/blink/renderer/core/dom/document_fragment.idl b/third_party/blink/renderer/core/dom/document_fragment.idl
index 892a8fbc..e69c55f 100644
--- a/third_party/blink/renderer/core/dom/document_fragment.idl
+++ b/third_party/blink/renderer/core/dom/document_fragment.idl
@@ -21,7 +21,8 @@
 
 [
     Constructor,
-    ConstructorCallWith=Document
+    ConstructorCallWith=Document,
+    Exposed=Window
 ] interface DocumentFragment : Node {
 };
 
diff --git a/third_party/blink/renderer/core/dom/document_test.cc b/third_party/blink/renderer/core/dom/document_test.cc
index 88b9214..58b99582 100644
--- a/third_party/blink/renderer/core/dom/document_test.cc
+++ b/third_party/blink/renderer/core/dom/document_test.cc
@@ -1001,11 +1001,10 @@
 // Test fixture parameterized on whether the "IsolatedWorldCSP" feature is
 // enabled.
 class IsolatedWorldCSPTest : public DocumentTest,
-                             public testing::WithParamInterface<bool> {
+                             public testing::WithParamInterface<bool>,
+                             private ScopedIsolatedWorldCSPForTest {
  public:
-  IsolatedWorldCSPTest() {
-    RuntimeEnabledFeatures::SetIsolatedWorldCSPEnabled(GetParam());
-  }
+  IsolatedWorldCSPTest() : ScopedIsolatedWorldCSPForTest(GetParam()) {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(IsolatedWorldCSPTest);
@@ -1226,12 +1225,12 @@
  * Tests for viewport-fit propagation.
  */
 
-class ViewportFitDocumentTest : public DocumentTest {
+class ViewportFitDocumentTest : public DocumentTest,
+                                private ScopedDisplayCutoutAPIForTest {
  public:
+  ViewportFitDocumentTest() : ScopedDisplayCutoutAPIForTest(true) {}
   void SetUp() override {
     DocumentTest::SetUp();
-
-    RuntimeEnabledFeatures::SetDisplayCutoutAPIEnabled(true);
     GetDocument().GetSettings()->SetViewportMetaEnabled(true);
   }
 
diff --git a/third_party/blink/renderer/core/dom/document_type.idl b/third_party/blink/renderer/core/dom/document_type.idl
index 836148a..dea1eca0 100644
--- a/third_party/blink/renderer/core/dom/document_type.idl
+++ b/third_party/blink/renderer/core/dom/document_type.idl
@@ -19,7 +19,9 @@
 
 // https://dom.spec.whatwg.org/#interface-documenttype
 
-interface DocumentType : Node {
+[
+    Exposed=Window
+] interface DocumentType : Node {
     readonly attribute DOMString name;
     readonly attribute DOMString publicId;
     readonly attribute DOMString systemId;
diff --git a/third_party/blink/renderer/core/dom/dom_implementation.idl b/third_party/blink/renderer/core/dom/dom_implementation.idl
index b70e5421..73eeb6ae 100644
--- a/third_party/blink/renderer/core/dom/dom_implementation.idl
+++ b/third_party/blink/renderer/core/dom/dom_implementation.idl
@@ -20,7 +20,9 @@
 
 // https://dom.spec.whatwg.org/#interface-domimplementation
 
-interface DOMImplementation {
+[
+    Exposed=Window
+] interface DOMImplementation {
     [NewObject, RaisesException] DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId, DOMString systemId);
     [NewObject, RaisesException] XMLDocument createDocument(DOMString? namespaceURI, [TreatNullAs=EmptyString] DOMString qualifiedName, optional DocumentType? doctype = null);
     // FIXME: createHTMLDocument should return a Document. crbug.com/238368
diff --git a/third_party/blink/renderer/core/dom/dom_string_map.idl b/third_party/blink/renderer/core/dom/dom_string_map.idl
index 4c3ef5c..8f47e3b 100644
--- a/third_party/blink/renderer/core/dom/dom_string_map.idl
+++ b/third_party/blink/renderer/core/dom/dom_string_map.idl
@@ -26,7 +26,8 @@
 // https://html.spec.whatwg.org/C/#the-domstringmap-interface
 
 [
-    OverrideBuiltins
+    OverrideBuiltins,
+    Exposed=Window
 ] interface DOMStringMap {
     [ImplementedAs=item] getter DOMString (DOMString name);
     [CEReactions, RaisesException] setter void (DOMString name, DOMString value);
diff --git a/third_party/blink/renderer/core/dom/element.idl b/third_party/blink/renderer/core/dom/element.idl
index e133a96..354677a 100644
--- a/third_party/blink/renderer/core/dom/element.idl
+++ b/third_party/blink/renderer/core/dom/element.idl
@@ -26,7 +26,9 @@
 
 // https://dom.spec.whatwg.org/#interface-element
 
-interface Element : Node {
+[
+    Exposed=Window
+] interface Element : Node {
     readonly attribute DOMString? namespaceURI;
     readonly attribute DOMString? prefix;
     readonly attribute DOMString localName;
diff --git a/third_party/blink/renderer/core/dom/idle_deadline.idl b/third_party/blink/renderer/core/dom/idle_deadline.idl
index 806691f..d245dab9 100644
--- a/third_party/blink/renderer/core/dom/idle_deadline.idl
+++ b/third_party/blink/renderer/core/dom/idle_deadline.idl
@@ -4,7 +4,9 @@
 
 // https://w3c.github.io/requestidlecallback/#window_extensions
 
-interface IdleDeadline {
+[
+    Exposed=Window
+] interface IdleDeadline {
     double timeRemaining();
     readonly attribute boolean didTimeout;
 };
diff --git a/third_party/blink/renderer/core/dom/mutation_record.idl b/third_party/blink/renderer/core/dom/mutation_record.idl
index 72315000..03608f6 100644
--- a/third_party/blink/renderer/core/dom/mutation_record.idl
+++ b/third_party/blink/renderer/core/dom/mutation_record.idl
@@ -30,7 +30,9 @@
 
 // https://dom.spec.whatwg.org/#interface-mutationrecord
 
-interface MutationRecord {
+[
+    Exposed=Window
+] interface MutationRecord {
     readonly attribute DOMString type;
     readonly attribute Node target;
     [SameObject] readonly attribute NodeList addedNodes;
diff --git a/third_party/blink/renderer/core/dom/named_node_map.idl b/third_party/blink/renderer/core/dom/named_node_map.idl
index bbb53e84..13482eb 100644
--- a/third_party/blink/renderer/core/dom/named_node_map.idl
+++ b/third_party/blink/renderer/core/dom/named_node_map.idl
@@ -21,7 +21,8 @@
 // https://dom.spec.whatwg.org/#interface-namednodemap
 
 [
-    LegacyUnenumerableNamedProperties
+    LegacyUnenumerableNamedProperties,
+    Exposed=Window
 ] interface NamedNodeMap {
     [Affects=Nothing] readonly attribute unsigned long length;
     [Affects=Nothing, MeasureAs=NamedNodeMapItem] getter Attr? item(unsigned long index);
diff --git a/third_party/blink/renderer/core/dom/node.idl b/third_party/blink/renderer/core/dom/node.idl
index 73f5989d..758feb3 100644
--- a/third_party/blink/renderer/core/dom/node.idl
+++ b/third_party/blink/renderer/core/dom/node.idl
@@ -20,7 +20,9 @@
 
 // https://dom.spec.whatwg.org/#interface-node
 
-interface Node : EventTarget {
+[
+    Exposed=Window
+] interface Node : EventTarget {
     const unsigned short ELEMENT_NODE = 1;
     const unsigned short ATTRIBUTE_NODE = 2;
     const unsigned short TEXT_NODE = 3;
diff --git a/third_party/blink/renderer/core/dom/node_filter.idl b/third_party/blink/renderer/core/dom/node_filter.idl
index e8ca30b8..f7ce8bcd 100644
--- a/third_party/blink/renderer/core/dom/node_filter.idl
+++ b/third_party/blink/renderer/core/dom/node_filter.idl
@@ -21,7 +21,8 @@
 // https://dom.spec.whatwg.org/#interface-nodefilter
 
 [
-    DoNotCheckConstants
+    DoNotCheckConstants,
+    Exposed=Window
 ] callback interface NodeFilter {
     // Constants for acceptNode()
     const unsigned short FILTER_ACCEPT = 1;
diff --git a/third_party/blink/renderer/core/dom/node_iterator.idl b/third_party/blink/renderer/core/dom/node_iterator.idl
index 54865148..2f88baf 100644
--- a/third_party/blink/renderer/core/dom/node_iterator.idl
+++ b/third_party/blink/renderer/core/dom/node_iterator.idl
@@ -20,7 +20,9 @@
 
 // https://dom.spec.whatwg.org/#interface-nodeiterator
 
-interface NodeIterator {
+[
+    Exposed=Window
+] interface NodeIterator {
     [SameObject] readonly attribute Node root;
     readonly attribute Node referenceNode;
     readonly attribute boolean pointerBeforeReferenceNode;
diff --git a/third_party/blink/renderer/core/dom/node_list.idl b/third_party/blink/renderer/core/dom/node_list.idl
index 405afd6..2c3f716 100644
--- a/third_party/blink/renderer/core/dom/node_list.idl
+++ b/third_party/blink/renderer/core/dom/node_list.idl
@@ -20,7 +20,9 @@
 
 // https://dom.spec.whatwg.org/#interface-nodelist
 
-interface NodeList {
+[
+    Exposed=Window
+] interface NodeList {
     [Affects=Nothing] getter Node? item(unsigned long index);
     [Affects=Nothing] readonly attribute unsigned long length;
     iterable<Node>;
diff --git a/third_party/blink/renderer/core/dom/processing_instruction.idl b/third_party/blink/renderer/core/dom/processing_instruction.idl
index a58cfbf..d7a40a30 100644
--- a/third_party/blink/renderer/core/dom/processing_instruction.idl
+++ b/third_party/blink/renderer/core/dom/processing_instruction.idl
@@ -20,7 +20,9 @@
 
 // https://dom.spec.whatwg.org/#interface-processinginstruction
 
-interface ProcessingInstruction : CharacterData {
+[
+    Exposed=Window
+] interface ProcessingInstruction : CharacterData {
     readonly attribute DOMString target;
 
     // ProcessingInstruction includes LinkStyle
diff --git a/third_party/blink/renderer/core/dom/range.idl b/third_party/blink/renderer/core/dom/range.idl
index 2095976..c2ee325 100644
--- a/third_party/blink/renderer/core/dom/range.idl
+++ b/third_party/blink/renderer/core/dom/range.idl
@@ -21,7 +21,8 @@
 // https://dom.spec.whatwg.org/#interface-range
 [
     Constructor,
-    ConstructorCallWith=Document
+    ConstructorCallWith=Document,
+    Exposed=Window
 ] interface Range {
     readonly attribute Node startContainer;
     readonly attribute unsigned long startOffset;
diff --git a/third_party/blink/renderer/core/dom/scripted_task_queue.idl b/third_party/blink/renderer/core/dom/scripted_task_queue.idl
index 1ce8f7a..df4c298 100644
--- a/third_party/blink/renderer/core/dom/scripted_task_queue.idl
+++ b/third_party/blink/renderer/core/dom/scripted_task_queue.idl
@@ -4,6 +4,9 @@
 
 callback TaskQueuePostCallback = void ();
 
-[RuntimeEnabled=ScriptedTaskQueue] interface ScriptedTaskQueue {
+[
+    RuntimeEnabled=ScriptedTaskQueue,
+    Exposed=Window
+] interface ScriptedTaskQueue {
     [CallWith=ScriptState] Promise<any> postTask(TaskQueuePostCallback callback, optional AbortSignal signal = null);
 };
diff --git a/third_party/blink/renderer/core/dom/scripted_task_queue_controller.idl b/third_party/blink/renderer/core/dom/scripted_task_queue_controller.idl
index 4701765..bda09c2 100644
--- a/third_party/blink/renderer/core/dom/scripted_task_queue_controller.idl
+++ b/third_party/blink/renderer/core/dom/scripted_task_queue_controller.idl
@@ -4,6 +4,9 @@
 
 enum MainThreadTaskQueueType { "user-interaction", "best-effort" };
 
-[RuntimeEnabled=ScriptedTaskQueue] interface ScriptedTaskQueueController {
+[
+    RuntimeEnabled=ScriptedTaskQueue,
+    Exposed=Window
+] interface ScriptedTaskQueueController {
     [ImplementedAs=defaultQueue] ScriptedTaskQueue default(MainThreadTaskQueueType queue_type);
 };
diff --git a/third_party/blink/renderer/core/dom/static_range.idl b/third_party/blink/renderer/core/dom/static_range.idl
index 71005a55..0aa967a 100644
--- a/third_party/blink/renderer/core/dom/static_range.idl
+++ b/third_party/blink/renderer/core/dom/static_range.idl
@@ -4,7 +4,9 @@
 
 // https://w3c.github.io/staticrange/#interface-staticrange
 
-interface StaticRange {
+[
+    Exposed=Window
+] interface StaticRange {
     readonly attribute Node startContainer;
     readonly attribute unsigned long startOffset;
     readonly attribute Node endContainer;
diff --git a/third_party/blink/renderer/core/dom/text.idl b/third_party/blink/renderer/core/dom/text.idl
index 36f98f54..d199fd7 100644
--- a/third_party/blink/renderer/core/dom/text.idl
+++ b/third_party/blink/renderer/core/dom/text.idl
@@ -21,7 +21,8 @@
 
 [
     Constructor(optional DOMString data = ""),
-    ConstructorCallWith=Document
+    ConstructorCallWith=Document,
+    Exposed=Window
 ] interface Text : CharacterData {
     [NewObject, DoNotTestNewObject, RaisesException] Text splitText(unsigned long offset);
     [MeasureAs=TextWholeText] readonly attribute DOMString wholeText;
diff --git a/third_party/blink/renderer/core/dom/tree_walker.idl b/third_party/blink/renderer/core/dom/tree_walker.idl
index 523ca96c..6f4f65cc 100644
--- a/third_party/blink/renderer/core/dom/tree_walker.idl
+++ b/third_party/blink/renderer/core/dom/tree_walker.idl
@@ -20,7 +20,9 @@
 
 // https://dom.spec.whatwg.org/#interface-treewalker
 
-interface TreeWalker {
+[
+    Exposed=Window
+] interface TreeWalker {
     [SameObject] readonly attribute Node root;
     readonly attribute unsigned long whatToShow;
     readonly attribute NodeFilter? filter;
diff --git a/third_party/blink/renderer/core/dom/xml_document.idl b/third_party/blink/renderer/core/dom/xml_document.idl
index df60667a..77ac609 100644
--- a/third_party/blink/renderer/core/dom/xml_document.idl
+++ b/third_party/blink/renderer/core/dom/xml_document.idl
@@ -25,5 +25,7 @@
 
 // https://dom.spec.whatwg.org/#xmldocument
 
-interface XMLDocument : Document {
+[
+    Exposed=Window
+] interface XMLDocument : Document {
 };
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index a2b59917..3714a5c 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -12777,8 +12777,7 @@
 
 TEST_P(ExternallyHandledPluginDocumentTest, DocumentType) {
   bool cross_process = GetParam();
-  RuntimeEnabledFeatures::SetMimeHandlerViewInCrossProcessFrameEnabled(
-      cross_process);
+  ScopedMimeHandlerViewInCrossProcessFrameForTest scoped_feature(cross_process);
   ScopedFakePluginRegistry fake_plugins;
   RegisterMockedHttpURLLoadWithMimeType("test.pdf", "application/pdf");
   frame_test_helpers::WebViewHelper web_view_helper;
diff --git a/third_party/blink/renderer/core/frame/reporting_context.cc b/third_party/blink/renderer/core/frame/reporting_context.cc
index 064cdaa..2702288 100644
--- a/third_party/blink/renderer/core/frame/reporting_context.cc
+++ b/third_party/blink/renderer/core/frame/reporting_context.cc
@@ -43,7 +43,7 @@
 
   // Buffer the report.
   if (!report_buffer_.Contains(report->type()))
-    report_buffer_.insert(report->type(), HeapLinkedHashSet<Member<Report>>());
+    report_buffer_.insert(report->type(), HeapListHashSet<Member<Report>>());
   report_buffer_.find(report->type())->value.insert(report);
 
   // Only the most recent 100 reports will remain buffered, per report type.
diff --git a/third_party/blink/renderer/core/frame/reporting_context.h b/third_party/blink/renderer/core/frame/reporting_context.h
index 8a0b98f..fab504ba 100644
--- a/third_party/blink/renderer/core/frame/reporting_context.h
+++ b/third_party/blink/renderer/core/frame/reporting_context.h
@@ -53,8 +53,8 @@
   // Send |report| via the Reporting API to |endpoint|.
   void SendToReportingAPI(Report* report, const String& endpoint) const;
 
-  HeapLinkedHashSet<Member<ReportingObserver>> observers_;
-  HeapHashMap<String, HeapLinkedHashSet<Member<Report>>> report_buffer_;
+  HeapListHashSet<Member<ReportingObserver>> observers_;
+  HeapHashMap<String, HeapListHashSet<Member<Report>>> report_buffer_;
   Member<ExecutionContext> execution_context_;
 
   // This is declared mutable so that the service endpoint can be cached by
diff --git a/third_party/blink/renderer/core/html/html_meta_element_test.cc b/third_party/blink/renderer/core/html/html_meta_element_test.cc
index 97d9bf78..c173147 100644
--- a/third_party/blink/renderer/core/html/html_meta_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_meta_element_test.cc
@@ -18,18 +18,23 @@
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 
-class HTMLMetaElementTest : public PageTestBase {
+class HTMLMetaElementTest : public PageTestBase,
+                            private ScopedDisplayCutoutAPIForTest,
+                            private ScopedMetaColorSchemeForTest,
+                            private ScopedMediaQueryPrefersColorSchemeForTest,
+                            private ScopedCSSColorSchemeForTest {
  public:
+  HTMLMetaElementTest()
+      : ScopedDisplayCutoutAPIForTest(true),
+        ScopedMetaColorSchemeForTest(true),
+        ScopedMediaQueryPrefersColorSchemeForTest(true),
+        ScopedCSSColorSchemeForTest(true) {}
   void SetUp() override {
     PageTestBase::SetUp();
-
-    RuntimeEnabledFeatures::SetDisplayCutoutAPIEnabled(true);
-    RuntimeEnabledFeatures::SetMetaColorSchemeEnabled(true);
-    RuntimeEnabledFeatures::SetMediaQueryPrefersColorSchemeEnabled(true);
-    RuntimeEnabledFeatures::SetCSSColorSchemeEnabled(true);
     GetDocument().GetSettings()->SetViewportMetaEnabled(true);
   }
 
@@ -164,7 +169,7 @@
   GetDocument().getElementById("meta")->removeAttribute(
       html_names::kContentAttr);
 
-  ExpectComputedColorScheme("auto");
+  ExpectComputedColorScheme("normal");
 }
 
 TEST_F(HTMLMetaElementTest, ColorSchemeProcessing_RemoveNameAttribute) {
@@ -176,17 +181,17 @@
 
   GetDocument().getElementById("meta")->removeAttribute(html_names::kNameAttr);
 
-  ExpectComputedColorScheme("auto");
+  ExpectComputedColorScheme("normal");
 }
 
 TEST_F(HTMLMetaElementTest, ColorSchemeParsing) {
   GetDocument().head()->AppendChild(CreateColorSchemeMeta(""));
 
   SetColorScheme("");
-  ExpectComputedColorScheme("auto");
+  ExpectComputedColorScheme("normal");
 
-  SetColorScheme("auto");
-  ExpectComputedColorScheme("auto");
+  SetColorScheme("normal");
+  ExpectComputedColorScheme("normal");
 
   SetColorScheme("light");
   ExpectComputedColorScheme("light");
@@ -201,19 +206,19 @@
   ExpectComputedColorScheme("BLUE light");
 
   SetColorScheme("light,dark");
-  ExpectComputedColorScheme("auto");
+  ExpectComputedColorScheme("normal");
 
   SetColorScheme("light,");
-  ExpectComputedColorScheme("auto");
+  ExpectComputedColorScheme("normal");
 
   SetColorScheme(",light");
-  ExpectComputedColorScheme("auto");
+  ExpectComputedColorScheme("normal");
 
   SetColorScheme(", light");
-  ExpectComputedColorScheme("auto");
+  ExpectComputedColorScheme("normal");
 
   SetColorScheme("light, dark");
-  ExpectComputedColorScheme("auto");
+  ExpectComputedColorScheme("normal");
 }
 
 TEST_F(HTMLMetaElementTest, ColorSchemeForcedDarkeningAndMQ) {
diff --git a/third_party/blink/renderer/core/input/event_handler_test.cc b/third_party/blink/renderer/core/input/event_handler_test.cc
index 20325a1f..9cb23518 100644
--- a/third_party/blink/renderer/core/input/event_handler_test.cc
+++ b/third_party/blink/renderer/core/input/event_handler_test.cc
@@ -38,6 +38,7 @@
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 #include "third_party/blink/renderer/platform/keyboard_codes.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 
 namespace blink {
@@ -1457,7 +1458,7 @@
 // Test that the hover is updated at the next begin frame after the compositor
 // scroll ends.
 TEST_F(EventHandlerSimTest, TestUpdateHoverAfterCompositorScrollAtBeginFrame) {
-  RuntimeEnabledFeatures::SetUpdateHoverFromScrollAtBeginFrameEnabled(true);
+  ScopedUpdateHoverFromScrollAtBeginFrameForTest scoped_feature(true);
   WebView().MainFrameWidget()->Resize(WebSize(800, 600));
   SimRequest request("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
@@ -1543,7 +1544,7 @@
 // Test that the hover is updated at the next begin frame after the main thread
 // scroll ends.
 TEST_F(EventHandlerSimTest, TestUpdateHoverAfterMainThreadScrollAtBeginFrame) {
-  RuntimeEnabledFeatures::SetUpdateHoverFromScrollAtBeginFrameEnabled(true);
+  ScopedUpdateHoverFromScrollAtBeginFrameForTest scoped_feature(true);
   WebView().MainFrameWidget()->Resize(WebSize(800, 600));
   SimRequest request("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
@@ -1644,7 +1645,7 @@
 // Test that the hover is updated at the next begin frame after the smooth JS
 // scroll ends.
 TEST_F(EventHandlerSimTest, TestUpdateHoverAfterJSScrollAtBeginFrame) {
-  RuntimeEnabledFeatures::SetUpdateHoverFromScrollAtBeginFrameEnabled(true);
+  ScopedUpdateHoverFromScrollAtBeginFrameForTest scoped_feature(true);
   WebView().MainFrameWidget()->Resize(WebSize(800, 500));
   SimRequest request("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
diff --git a/third_party/blink/renderer/core/input/fallback_cursor_event_manager_test.cc b/third_party/blink/renderer/core/input/fallback_cursor_event_manager_test.cc
index c7786a3dc..8b8cda46 100644
--- a/third_party/blink/renderer/core/input/fallback_cursor_event_manager_test.cc
+++ b/third_party/blink/renderer/core/input/fallback_cursor_event_manager_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/input/event_handler.h"
 #include "third_party/blink/renderer/core/loader/empty_clients.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 
@@ -41,13 +42,13 @@
   DISALLOW_COPY_AND_ASSIGN(FallbackCursorChromeClient);
 };
 
-class FallbackCursorEventManagerTest : public RenderingTest {
+class FallbackCursorEventManagerTest : public RenderingTest,
+                                       private ScopedFallbackCursorModeForTest {
  protected:
   FallbackCursorEventManagerTest()
       : RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()),
-        chrome_client_(MakeGarbageCollected<FallbackCursorChromeClient>()) {
-    RuntimeEnabledFeatures::SetFallbackCursorModeEnabled(true);
-  }
+        ScopedFallbackCursorModeForTest(true),
+        chrome_client_(MakeGarbageCollected<FallbackCursorChromeClient>()) {}
 
   ~FallbackCursorEventManagerTest() override {}
 
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager_test.cc b/third_party/blink/renderer/core/input/pointer_event_manager_test.cc
index 8d7a7580..3c3d95c 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager_test.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/input/event_handler.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 
@@ -245,59 +246,63 @@
   GetDocument().body()->addEventListener(event_type_names::kPointermove,
                                          callback);
 
-  // Turn on the flag for test.
-  RuntimeEnabledFeatures::SetMovementXYInBlinkEnabled(true);
+  {
+    // Turn on the flag for test.
+    ScopedMovementXYInBlinkForTest scoped_feature(true);
 
-  WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
-      CreateTestPointerEvent(WebInputEvent::kPointerMove,
-                             WebPointerProperties::PointerType::kMouse,
-                             WebFloatPoint(150, 210), WebFloatPoint(100, 50),
-                             10, 10),
-      std::vector<WebPointerEvent>(), std::vector<WebPointerEvent>()));
-  // The first pointermove event has movement_x/y 0.
-  ASSERT_EQ(callback->last_screen_x_, 100);
-  ASSERT_EQ(callback->last_screen_y_, 50);
-  ASSERT_EQ(callback->last_movement_x_, 0);
-  ASSERT_EQ(callback->last_movement_y_, 0);
+    WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
+        CreateTestPointerEvent(WebInputEvent::kPointerMove,
+                               WebPointerProperties::PointerType::kMouse,
+                               WebFloatPoint(150, 210), WebFloatPoint(100, 50),
+                               10, 10),
+        std::vector<WebPointerEvent>(), std::vector<WebPointerEvent>()));
+    // The first pointermove event has movement_x/y 0.
+    ASSERT_EQ(callback->last_screen_x_, 100);
+    ASSERT_EQ(callback->last_screen_y_, 50);
+    ASSERT_EQ(callback->last_movement_x_, 0);
+    ASSERT_EQ(callback->last_movement_y_, 0);
 
-  WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
-      CreateTestPointerEvent(WebInputEvent::kPointerMove,
-                             WebPointerProperties::PointerType::kMouse,
-                             WebFloatPoint(150, 200), WebFloatPoint(132, 29),
-                             10, 10),
-      std::vector<WebPointerEvent>(), std::vector<WebPointerEvent>()));
-  // pointermove event movement = event.screenX/Y - last_event.screenX/Y.
-  ASSERT_EQ(callback->last_screen_x_, 132);
-  ASSERT_EQ(callback->last_screen_y_, 29);
-  ASSERT_EQ(callback->last_movement_x_, 32);
-  ASSERT_EQ(callback->last_movement_y_, -21);
+    WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
+        CreateTestPointerEvent(WebInputEvent::kPointerMove,
+                               WebPointerProperties::PointerType::kMouse,
+                               WebFloatPoint(150, 200), WebFloatPoint(132, 29),
+                               10, 10),
+        std::vector<WebPointerEvent>(), std::vector<WebPointerEvent>()));
+    // pointermove event movement = event.screenX/Y - last_event.screenX/Y.
+    ASSERT_EQ(callback->last_screen_x_, 132);
+    ASSERT_EQ(callback->last_screen_y_, 29);
+    ASSERT_EQ(callback->last_movement_x_, 32);
+    ASSERT_EQ(callback->last_movement_y_, -21);
 
-  WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
-      CreateTestPointerEvent(WebInputEvent::kPointerMove,
-                             WebPointerProperties::PointerType::kMouse,
-                             WebFloatPoint(150, 210),
-                             WebFloatPoint(113.8, 32.7), 10, 10),
-      std::vector<WebPointerEvent>(), std::vector<WebPointerEvent>()));
-  // fractional screen coordinates result in fractional movement.
-  ASSERT_FLOAT_EQ(callback->last_screen_x_, 113.8);
-  ASSERT_FLOAT_EQ(callback->last_screen_y_, 32.7);
-  // TODO(eirage): These should be float value once mouse_event.idl change.
-  ASSERT_FLOAT_EQ(callback->last_movement_x_, -18);
-  ASSERT_FLOAT_EQ(callback->last_movement_y_, 3);
+    WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
+        CreateTestPointerEvent(WebInputEvent::kPointerMove,
+                               WebPointerProperties::PointerType::kMouse,
+                               WebFloatPoint(150, 210),
+                               WebFloatPoint(113.8, 32.7), 10, 10),
+        std::vector<WebPointerEvent>(), std::vector<WebPointerEvent>()));
+    // fractional screen coordinates result in fractional movement.
+    ASSERT_FLOAT_EQ(callback->last_screen_x_, 113.8);
+    ASSERT_FLOAT_EQ(callback->last_screen_y_, 32.7);
+    // TODO(eirage): These should be float value once mouse_event.idl change.
+    ASSERT_FLOAT_EQ(callback->last_movement_x_, -18);
+    ASSERT_FLOAT_EQ(callback->last_movement_y_, 3);
+  }
 
-  // When flag is off, movementX/Y follows the value in WebPointerProperties.
-  RuntimeEnabledFeatures::SetMovementXYInBlinkEnabled(false);
+  {
+    // When flag is off, movementX/Y follows the value in WebPointerProperties.
+    ScopedMovementXYInBlinkForTest scoped_feature(false);
 
-  WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
-      CreateTestPointerEvent(WebInputEvent::kPointerMove,
-                             WebPointerProperties::PointerType::kMouse,
-                             WebFloatPoint(150, 210), WebFloatPoint(100, 16.25),
-                             1024, -8765),
-      std::vector<WebPointerEvent>(), std::vector<WebPointerEvent>()));
-  ASSERT_EQ(callback->last_screen_x_, 100);
-  ASSERT_EQ(callback->last_screen_y_, 16.25);
-  ASSERT_EQ(callback->last_movement_x_, 1024);
-  ASSERT_EQ(callback->last_movement_y_, -8765);
+    WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
+        CreateTestPointerEvent(WebInputEvent::kPointerMove,
+                               WebPointerProperties::PointerType::kMouse,
+                               WebFloatPoint(150, 210),
+                               WebFloatPoint(100, 16.25), 1024, -8765),
+        std::vector<WebPointerEvent>(), std::vector<WebPointerEvent>()));
+    ASSERT_EQ(callback->last_screen_x_, 100);
+    ASSERT_EQ(callback->last_screen_y_, 16.25);
+    ASSERT_EQ(callback->last_movement_x_, 1024);
+    ASSERT_EQ(callback->last_movement_y_, -8765);
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 9697b7d..8c78317 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -797,6 +797,20 @@
   return nullptr;
 }
 
+LayoutBlockFlow* LayoutObject::RootInlineFormattingContext() const {
+  DCHECK(IsInline());
+  for (LayoutObject* parent = Parent(); parent; parent = parent->Parent()) {
+    if (auto* block_flow = DynamicTo<LayoutBlockFlow>(parent)) {
+      // Skip |LayoutFlowThread| because it is skipped when finding the first
+      // child in |GetLayoutObjectForFirstChildNode|.
+      if (UNLIKELY(block_flow->IsLayoutFlowThread()))
+        return DynamicTo<LayoutBlockFlow>(block_flow->Parent());
+      return block_flow;
+    }
+  }
+  return nullptr;
+}
+
 LayoutBlockFlow* LayoutObject::ContainingNGBlockFlow() const {
   DCHECK(IsInline());
   if (!RuntimeEnabledFeatures::LayoutNGEnabled())
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index a3261a9..ebafbbb 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -363,6 +363,11 @@
 
   LayoutBox* EnclosingScrollableBox() const;
 
+  // Returns the root of the inline formatting context |this| belongs to. |this|
+  // must be |IsInline()|. The root is the object that holds |NGPaintFragment|
+  // if it's in LayoutNG context.
+  LayoutBlockFlow* RootInlineFormattingContext() const;
+
   // Returns the containing block flow if it's a LayoutNGBlockFlow, or nullptr
   // otherwise. Note that the semantics is different from |EnclosingBox| for
   // atomic inlines that this function returns the container, while
diff --git a/third_party/blink/renderer/core/layout/layout_object_child_list.cc b/third_party/blink/renderer/core/layout/layout_object_child_list.cc
index 70e945f..1e3b8cf8 100644
--- a/third_party/blink/renderer/core/layout/layout_object_child_list.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_child_list.cc
@@ -47,6 +47,7 @@
   if (auto* layout_text = ToLayoutTextOrNull(object)) {
     layout_text->SetFirstInlineFragment(nullptr);
     layout_text->InvalidateInlineItems();
+    layout_text->SetIsInLayoutNGInlineFormattingContext(false);
     return;
   }
 
@@ -61,10 +62,12 @@
       if (child->IsInLayoutNGInlineFormattingContext())
         InvalidateInlineItems(child);
     }
+    layout_inline->SetIsInLayoutNGInlineFormattingContext(false);
     return;
   }
 
   object->SetFirstInlineFragment(nullptr);
+  object->SetIsInLayoutNGInlineFormattingContext(false);
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/core/layout/layout_theme_test.cc b/third_party/blink/renderer/core/layout/layout_theme_test.cc
index c2eef23..b699842f 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_test.cc
@@ -17,15 +17,14 @@
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 
-class LayoutThemeTest : public PageTestBase {
+class LayoutThemeTest : public PageTestBase,
+                        private ScopedCSSColorSchemeForTest {
  protected:
-  void SetUp() override {
-    RuntimeEnabledFeatures::SetCSSColorSchemeEnabled(true);
-    PageTestBase::SetUp();
-  }
+  LayoutThemeTest() : ScopedCSSColorSchemeForTest(true) {}
   void SetHtmlInnerHTML(const char* html_content);
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc
index faf30b8..987d5b5c 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc
@@ -73,7 +73,7 @@
       end_collapse_type_(kNotCollapsible),
       is_end_collapsible_newline_(false),
       is_symbol_marker_(false),
-      is_generated_(false) {
+      is_generated_for_line_break_(false) {
   DCHECK_GE(end, start);
   ComputeBoxProperties();
 }
@@ -95,7 +95,7 @@
       end_collapse_type_(other.end_collapse_type_),
       is_end_collapsible_newline_(other.is_end_collapsible_newline_),
       is_symbol_marker_(other.is_symbol_marker_),
-      is_generated_(other.is_generated_) {
+      is_generated_for_line_break_(other.is_generated_for_line_break_) {
   DCHECK_GE(end, start);
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h
index 4b63469e..3ef55a46 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h
@@ -175,8 +175,8 @@
   // context that are lost during the whitespace collapsing. This item is used
   // during the line breaking and layout, but is not supposed to generate
   // fragments.
-  bool IsGenerated() const { return is_generated_; }
-  void SetIsGenerated() { is_generated_ = true; }
+  bool IsGeneratedForLineBreak() const { return is_generated_for_line_break_; }
+  void SetIsGeneratedForLineBreak() { is_generated_for_line_break_ = true; }
 
   // Whether the end collapsible space run contains a newline.
   // Valid only when kCollapsible or kCollapsed.
@@ -243,7 +243,7 @@
   unsigned end_collapse_type_ : 2;  // NGCollapseType
   unsigned is_end_collapsible_newline_ : 1;
   unsigned is_symbol_marker_ : 1;
-  unsigned is_generated_ : 1;
+  unsigned is_generated_for_line_break_ : 1;
   friend class NGInlineNode;
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc
index 77cfcaf..801aaf3 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc
@@ -110,6 +110,38 @@
   return StartOffset();
 }
 
+bool NGLineInfo::ShouldHangTrailingSpaces() const {
+  DCHECK(HasTrailingSpaces());
+  if (!line_style_->AutoWrap())
+    return false;
+  switch (text_align_) {
+    case ETextAlign::kStart:
+    case ETextAlign::kJustify:
+      return true;
+    case ETextAlign::kEnd:
+    case ETextAlign::kCenter:
+    case ETextAlign::kWebkitCenter:
+      return false;
+    case ETextAlign::kLeft:
+    case ETextAlign::kWebkitLeft:
+      return IsLtr(BaseDirection());
+    case ETextAlign::kRight:
+    case ETextAlign::kWebkitRight:
+      return IsRtl(BaseDirection());
+  }
+  NOTREACHED();
+}
+
+void NGLineInfo::UpdateTextAlign() {
+  text_align_ = line_style_->GetTextAlign(IsLastLine());
+
+  if (HasTrailingSpaces() && ShouldHangTrailingSpaces()) {
+    hang_width_ = ComputeTrailingSpaceWidth(&end_offset_for_justify_);
+  } else if (text_align_ == ETextAlign::kJustify) {
+    end_offset_for_justify_ = InflowEndOffset();
+  }
+}
+
 LayoutUnit NGLineInfo::ComputeTrailingSpaceWidth(
     unsigned* end_offset_out) const {
   if (!has_trailing_spaces_) {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h
index 9e0253f8..505bf23 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h
@@ -176,17 +176,22 @@
   void SetTextIndent(LayoutUnit indent) { text_indent_ = indent; }
   LayoutUnit TextIndent() const { return text_indent_; }
 
+  ETextAlign TextAlign() const { return text_align_; }
+  // Update |TextAlign()| and related fields. This depends on |IsLastLine()| and
+  // that must be called after |SetIsLastLine()|.
+  void UpdateTextAlign();
+
   NGBfcOffset BfcOffset() const { return bfc_offset_; }
   LayoutUnit AvailableWidth() const { return available_width_; }
 
   // The width of this line. Includes trailing spaces if they were preserved.
   // Negative width created by negative 'text-indent' is clamped to zero.
   LayoutUnit Width() const { return width_.ClampNegativeToZero(); }
-  // Same as |Width()| but returns negative value as is.
-  LayoutUnit WidthForAlignment() const { return width_; }
-  // The width of preserved trailing spaces.
-  LayoutUnit ComputeTrailingSpaceWidth(
-      unsigned* end_offset_out = nullptr) const;
+  // Same as |Width()| but returns negative value as is. Preserved trailing
+  // spaces may or may not be included, depends on |ShouldHangTrailingSpaces()|.
+  LayoutUnit WidthForAlignment() const { return width_ - hang_width_; }
+  // Width that hangs over the end of the line; e.g., preserved trailing spaces.
+  LayoutUnit HangWidth() const { return hang_width_; }
   // Compute |Width()| from |Results()|. Used during line breaking, before
   // |Width()| is set. After line breaking, this should match to |Width()|
   // without clamping.
@@ -194,6 +199,7 @@
 
   bool HasTrailingSpaces() const { return has_trailing_spaces_; }
   void SetHasTrailingSpaces() { has_trailing_spaces_ = true; }
+  bool ShouldHangTrailingSpaces() const;
 
   // True if this line has overflow, excluding preserved trailing spaces.
   bool HasOverflow() const { return has_overflow_; }
@@ -211,6 +217,12 @@
   // End text offset of this line, excluding out-of-flow objects such as
   // floating or positioned.
   unsigned InflowEndOffset() const;
+  // End text offset for `text-align: justify`. This excludes preserved trailing
+  // spaces. Available only when |TextAlign()| is |kJustify|.
+  unsigned EndOffsetForJustify() const {
+    DCHECK_EQ(text_align_, ETextAlign::kJustify);
+    return end_offset_for_justify_;
+  }
   // End item index of this line.
   unsigned EndItemIndex() const { return end_item_index_; }
   void SetEndItemIndex(unsigned index) { end_item_index_ = index; }
@@ -234,6 +246,10 @@
  private:
   bool ComputeNeedsAccurateEndPosition() const;
 
+  // The width of preserved trailing spaces.
+  LayoutUnit ComputeTrailingSpaceWidth(
+      unsigned* end_offset_out = nullptr) const;
+
   const NGInlineItemsData* items_data_ = nullptr;
   const ComputedStyle* line_style_ = nullptr;
   NGInlineItemResults results_;
@@ -243,11 +259,14 @@
 
   LayoutUnit available_width_;
   LayoutUnit width_;
+  LayoutUnit hang_width_;
   LayoutUnit text_indent_;
 
   unsigned start_offset_;
   unsigned end_item_index_;
+  unsigned end_offset_for_justify_;
 
+  ETextAlign text_align_ = ETextAlign::kLeft;
   TextDirection base_direction_ = TextDirection::kLtr;
 
   bool use_first_line_style_ = false;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc
index cdd25b9..5f4b984b 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc
@@ -271,7 +271,7 @@
                                                        nullptr);
   AppendBreakOpportunity(layout_object);
   NGInlineItem* item = &items_->back();
-  item->SetIsGenerated();
+  item->SetIsGeneratedForLineBreak();
   item->SetEndCollapseType(NGInlineItem::kOpaqueToCollapsing);
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 39f4f0a..4007a0c 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -293,6 +293,14 @@
     box_states_->UpdateAfterReorder(&line_box_);
   }
   LayoutUnit inline_size = box_states_->ComputeInlinePositions(&line_box_);
+  if (LayoutUnit hang_width = line_info->HangWidth()) {
+    inline_size -= hang_width;
+    container_builder_.SetHangInlineSize(hang_width);
+
+    if (IsRtl(line_info->BaseDirection())) {
+      line_box_.MoveInInlineDirection(-hang_width);
+    }
+  }
 
   // Truncate the line if 'text-overflow: ellipsis' is set.
   if (UNLIKELY(inline_size > line_info->AvailableWidth() &&
@@ -391,7 +399,7 @@
     case kZeroWidthSpaceCharacter:
       // Don't generate fragments if this is a generated (not in DOM) break
       // opportunity during the white space collapsing in NGInlineItemBuilder.
-      if (item.IsGenerated())
+      if (item.IsGeneratedForLineBreak())
         return;
       type = NGPhysicalTextFragment::kFlowControl;
       break;
@@ -631,10 +639,7 @@
     return false;
 
   // Justify the end of visible text, ignoring preserved trailing spaces.
-  unsigned end_offset;
-  LayoutUnit trailing_spaces_width =
-      line_info->ComputeTrailingSpaceWidth(&end_offset);
-  space += trailing_spaces_width;
+  unsigned end_offset = line_info->EndOffsetForJustify();
 
   // If this line overflows, fallback to 'text-align: start'.
   if (space <= 0)
@@ -708,8 +713,7 @@
   LayoutUnit space =
       line_info->AvailableWidth() - line_info->WidthForAlignment();
 
-  const ComputedStyle& line_style = line_info->LineStyle();
-  ETextAlign text_align = line_style.GetTextAlign(line_info->IsLastLine());
+  ETextAlign text_align = line_info->TextAlign();
   if (text_align == ETextAlign::kJustify) {
     // If justification succeeds, no offset is needed. Expansions are set to
     // each |NGInlineItemResult| in |line_info|.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
index a6e42b72..13d2b5d6 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
@@ -56,6 +56,12 @@
 }
 
 void NGLineBoxFragmentBuilder::ChildList::MoveInInlineDirection(
+    LayoutUnit delta) {
+  for (auto& child : children_)
+    child.offset.inline_offset += delta;
+}
+
+void NGLineBoxFragmentBuilder::ChildList::MoveInInlineDirection(
     LayoutUnit delta,
     unsigned start,
     unsigned end) {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
index c08559b..133443a 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
@@ -50,6 +50,10 @@
     size_.inline_size = inline_size;
   }
 
+  void SetHangInlineSize(LayoutUnit hang_inline_size) {
+    hang_inline_size_ = hang_inline_size;
+  }
+
   // Mark this line box is an "empty" line box. See NGLineBoxType.
   void SetIsEmptyLineBox();
 
@@ -218,6 +222,7 @@
                                     inline_size, bidi_level});
     }
 
+    void MoveInInlineDirection(LayoutUnit);
     void MoveInInlineDirection(LayoutUnit, unsigned start, unsigned end);
     void MoveInBlockDirection(LayoutUnit);
     void MoveInBlockDirection(LayoutUnit, unsigned start, unsigned end);
@@ -234,6 +239,7 @@
 
  private:
   NGLineHeightMetrics metrics_;
+  LayoutUnit hang_inline_size_;
   NGPhysicalLineBoxFragment::NGLineBoxType line_box_type_;
   TextDirection base_direction_;
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
index 9682f1d..121de94 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
@@ -477,6 +477,8 @@
   line_info->SetWidth(available_width, position_);
   line_info->SetBfcOffset(
       {line_opportunity_.line_left_offset, line_opportunity_.bfc_block_offset});
+  if (mode_ == NGLineBreakerMode::kContent)
+    line_info->UpdateTextAlign();
 }
 
 void NGLineBreaker::HandleText(const NGInlineItem& item,
@@ -1074,7 +1076,11 @@
     case kZeroWidthSpaceCharacter: {
       // <wbr> tag creates break opportunities regardless of auto_wrap.
       NGInlineItemResult* item_result = AddItem(item, line_info);
-      item_result->should_create_line_box = true;
+      // A generated break opportunity doesn't generate fragments, but we still
+      // need to add this for rewind to find this opportunity. This will be
+      // discarded in |NGInlineLayoutAlgorithm| when it generates fragments.
+      if (!item.IsGeneratedForLineBreak())
+        item_result->should_create_line_box = true;
       item_result->can_break_after = true;
       break;
     }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc
index 7a1fc7f5..778bb89 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc
@@ -494,8 +494,13 @@
   )HTML");
 
   Vector<NGLineInfo> line_infos = BreakToLineInfo(node, LayoutUnit(50));
-  EXPECT_EQ(line_infos[0].ComputeTrailingSpaceWidth(),
-            LayoutUnit(10) * data.trailing_space_width);
+  const NGLineInfo& line_info = line_infos[0];
+  if (line_info.ShouldHangTrailingSpaces()) {
+    EXPECT_EQ(line_info.HangWidth(),
+              LayoutUnit(10) * data.trailing_space_width);
+  } else {
+    EXPECT_EQ(line_info.HangWidth(), LayoutUnit());
+  }
 }
 
 TEST_F(NGLineBreakerTest, MinMaxWithTrailingSpaces) {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
index fb153f3..0ac7582 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
@@ -64,6 +64,7 @@
   // A line box must have a metrics unless it's an empty line box.
   DCHECK(!metrics_.IsEmpty() || IsEmptyLineBox());
   base_direction_ = static_cast<unsigned>(builder->base_direction_);
+  has_hanging_ = builder->hang_inline_size_ != 0;
   has_propagated_descendants_ = has_floating_descendants_ ||
                                 !oof_positioned_descendants_.IsEmpty() ||
                                 builder->unpositioned_list_marker_;
@@ -88,6 +89,24 @@
     PhysicalRect child_scroll_overflow =
         child->ScrollableOverflowForPropagation(container);
     child_scroll_overflow.offset += child.Offset();
+
+    // Chop the hanging part from scrollable overflow. Children overflow in
+    // inline direction should hang, which should not cause scroll.
+    // TODO(kojii): Should move to text fragment to make this more accurate.
+    if (UNLIKELY(has_hanging_ && !child->IsFloatingOrOutOfFlowPositioned())) {
+      if (IsHorizontalWritingMode(container_writing_mode)) {
+        if (child_scroll_overflow.offset.left < 0)
+          child_scroll_overflow.offset.left = LayoutUnit();
+        if (child_scroll_overflow.Right() > Size().width)
+          child_scroll_overflow.ShiftRightEdgeTo(Size().width);
+      } else {
+        if (child_scroll_overflow.offset.top < 0)
+          child_scroll_overflow.offset.top = LayoutUnit();
+        if (child_scroll_overflow.Bottom() > Size().height)
+          child_scroll_overflow.ShiftBottomEdgeTo(Size().height);
+      }
+    }
+
     // If child has the same style as parent, parent will compute relative
     // offset.
     if (&child->Style() != container_style) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
index 86bf28e6..868db0e 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
@@ -301,6 +301,7 @@
   // (it's defined here to save memory, since that class has no bitfields).
   unsigned has_propagated_descendants_ : 1;
   unsigned base_direction_ : 1;  // TextDirection
+  unsigned has_hanging_ : 1;
 
   // The following bitfields are only to be used by NGPhysicalBoxFragment
   // (it's defined here to save memory, since that class has no bitfields).
diff --git a/third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info_test.cc b/third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info_test.cc
index 3787c9a..31ca25451 100644
--- a/third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info_test.cc
+++ b/third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info_test.cc
@@ -6,15 +6,16 @@
 
 #include "base/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 
-class AlternateSignedExchangeResourceInfoTest : public testing::Test {
+class AlternateSignedExchangeResourceInfoTest
+    : public testing::Test,
+      private ScopedSignedExchangeSubresourcePrefetchForTest {
  public:
-  AlternateSignedExchangeResourceInfoTest() {
-    RuntimeEnabledFeatures::SetSignedExchangeSubresourcePrefetchEnabled(true);
-  }
+  AlternateSignedExchangeResourceInfoTest()
+      : ScopedSignedExchangeSubresourcePrefetchForTest(true) {}
   ~AlternateSignedExchangeResourceInfoTest() override = default;
 
  protected:
diff --git a/third_party/blink/renderer/core/paint/inline_painter.cc b/third_party/blink/renderer/core/paint/inline_painter.cc
index 316a720..e292380 100644
--- a/third_party/blink/renderer/core/paint/inline_painter.cc
+++ b/third_party/blink/renderer/core/paint/inline_painter.cc
@@ -44,7 +44,7 @@
 
   if (layout_inline_.IsInLayoutNGInlineFormattingContext()) {
     for (const NGPaintFragment* fragment :
-         NGPaintFragment::InlineFragmentsFor(&layout_inline_)) {
+         NGPaintFragment::SafeInlineFragmentsFor(&layout_inline_)) {
       auto child_offset = paint_offset +
                           fragment->InlineOffsetToContainerBox() -
                           fragment->Offset();
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
index 450fcde..fe4581b7 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
@@ -493,6 +493,26 @@
   return FragmentRange(nullptr, false);
 }
 
+NGPaintFragment::FragmentRange NGPaintFragment::SafeInlineFragmentsFor(
+    const LayoutObject* layout_object) {
+  auto fragments = InlineFragmentsFor(layout_object);
+  if (!fragments.IsInLayoutNGInlineFormattingContext())
+    return fragments;
+
+  // TODO(kojii): If the block flow is dirty, children of these fragments
+  // maybe already deleted. crbug.com/963103
+  const LayoutBlockFlow* block_flow =
+      layout_object->RootInlineFormattingContext();
+  if (UNLIKELY(block_flow->NeedsLayout()))
+    return FragmentRange(nullptr);
+
+  // TODO(kojii): The root of this inline formatting context should have a
+  // PaintFragment, but it looks like there's a case it doesn't stand.
+  // crbug.com/969096
+  CHECK(block_flow->PaintFragment() || fragments.IsEmpty());
+  return fragments;
+}
+
 const NGPaintFragment* NGPaintFragment::LastForSameLayoutObject() const {
   return const_cast<NGPaintFragment*>(this)->LastForSameLayoutObject();
 }
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
index 9ca08b8..ca39aee 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
@@ -256,6 +256,10 @@
   // Returns a range of NGPaintFragment in an inline formatting context that are
   // for a LayoutObject.
   static FragmentRange InlineFragmentsFor(const LayoutObject*);
+  // A safer version that returns empty if the block flow is dirty.
+  // TODO(kojii): If the block flow is dirty, children of these fragments maybe
+  // already deleted. crbug.com/963103
+  static FragmentRange SafeInlineFragmentsFor(const LayoutObject*);
 
   // Same as |InlineFragmentsFor()| but this function includes descendants if
   // the |layout_object| is culled (i.e., did not generate fragments.)
diff --git a/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc b/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc
index 605a1b46..195292d 100644
--- a/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc
+++ b/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc
@@ -24,6 +24,7 @@
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "v8/include/v8.h"
 
@@ -599,7 +600,7 @@
 }
 
 TEST(ReadableStreamOperationsTest, Serialize) {
-  RuntimeEnabledFeatures::SetTransferableStreamsEnabled(true);
+  ScopedTransferableStreamsForTest enabled(true);
 
   V8TestingScope scope;
   TryCatchScope try_catch_scope(scope.GetIsolate());
diff --git a/third_party/blink/renderer/core/streams/readable_stream_test.cc b/third_party/blink/renderer/core/streams/readable_stream_test.cc
index 8347364a..656da9d 100644
--- a/third_party/blink/renderer/core/streams/readable_stream_test.cc
+++ b/third_party/blink/renderer/core/streams/readable_stream_test.cc
@@ -416,7 +416,6 @@
   }
 
   ScopedTransferableStreamsForTest enabled(true);
-  RuntimeEnabledFeatures::SetTransferableStreamsEnabled(true);
 
   V8TestingScope scope;
   auto* script_state = scope.GetScriptState();
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.cc b/third_party/blink/renderer/core/workers/dedicated_worker.cc
index 9c221ad..4b7d7970 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.cc
@@ -475,12 +475,11 @@
 
   // This worker is being created by an existing worker (i.e., nested workers).
   // Clone the worker fetch context from the parent's one.
-  // TODO(nhiroki): Create WebWorkerFetchContext using |factory_client_| when
-  // PlzDedicatedWorker is enabled (https://crbug.com/906991).
   auto* scope = To<WorkerGlobalScope>(GetExecutionContext());
-  return static_cast<WorkerFetchContext&>(scope->Fetcher()->Context())
-      .GetWebWorkerFetchContext()
-      ->CloneForNestedWorker(scope->GetTaskRunner(TaskType::kNetworking));
+  return factory_client_->CloneWorkerFetchContext(
+      static_cast<WorkerFetchContext&>(scope->Fetcher()->Context())
+          .GetWebWorkerFetchContext(),
+      scope->GetTaskRunner(TaskType::kNetworking));
 }
 
 const AtomicString& DedicatedWorker::InterfaceName() const {
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc
index 48323992..831fe0c 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc
@@ -199,10 +199,11 @@
       if (is_running) {
         state_ = STARTED;
         DCHECK(capture_params_ == new_capture_params);
-        OnStartDone(MEDIA_DEVICE_OK);
+        OnStartDone(mojom::blink::MediaStreamRequestResult::OK);
       } else {
         state_ = STOPPED;
-        OnStartDone(MEDIA_DEVICE_TRACK_START_FAILURE_VIDEO);
+        OnStartDone(
+            mojom::blink::MediaStreamRequestResult::TRACK_START_FAILURE_VIDEO);
       }
       break;
     case STARTED:
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
index f054ad08..4e639f5 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
@@ -11,6 +11,7 @@
 #include "media/base/bind_to_current_loop.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_sink.h"
@@ -174,7 +175,7 @@
 
  protected:
   void OnConstraintsApplied(WebPlatformMediaStreamSource* source,
-                            MediaStreamRequestResult result,
+                            mojom::blink::MediaStreamRequestResult result,
                             const WebString& result_name) {}
 
   ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_source.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_source.cc
index 520ac9f..fb1da29 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_source.cc
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_constraints_util_video_device.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
@@ -337,7 +338,8 @@
   SetReadyState(WebMediaStreamSource::kReadyStateEnded);
 }
 
-void MediaStreamVideoSource::OnStartDone(MediaStreamRequestResult result) {
+void MediaStreamVideoSource::OnStartDone(
+    mojom::blink::MediaStreamRequestResult result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DVLOG(3) << "OnStartDone({result =" << result << "})";
   if (state_ == ENDED) {
@@ -347,7 +349,7 @@
     return;
   }
 
-  if (result == MEDIA_DEVICE_OK) {
+  if (result == mojom::blink::MediaStreamRequestResult::OK) {
     DCHECK_EQ(STARTING, state_);
     OnLog("MediaStreamVideoSource changing state to STARTED");
     state_ = STARTED;
@@ -367,11 +369,13 @@
   std::vector<PendingTrackInfo> pending_track_descriptors;
   pending_track_descriptors.swap(pending_tracks_);
   for (const auto& track_info : pending_track_descriptors) {
-    MediaStreamRequestResult result = MEDIA_DEVICE_OK;
-    if (state_ != STARTED)
-      result = MEDIA_DEVICE_TRACK_START_FAILURE_VIDEO;
+    auto result = mojom::blink::MediaStreamRequestResult::OK;
+    if (state_ != STARTED) {
+      result =
+          mojom::blink::MediaStreamRequestResult::TRACK_START_FAILURE_VIDEO;
+    }
 
-    if (result == MEDIA_DEVICE_OK) {
+    if (result == mojom::blink::MediaStreamRequestResult::OK) {
       track_adapter_->AddTrack(track_info.track, track_info.frame_callback,
                                track_info.settings_callback,
                                track_info.format_callback,
diff --git a/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.cc b/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.cc
index 5f15cc5..9328634 100644
--- a/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.cc
+++ b/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/strings/string_number_conversions.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/payments/payment_request_event.cc b/third_party/blink/renderer/modules/payments/payment_request_event.cc
index 6eee25a..2408bfb 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_event.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request_event.cc
@@ -15,7 +15,8 @@
 #include "third_party/blink/renderer/modules/payments/payment_method_change_response.h"
 #include "third_party/blink/renderer/modules/payments/payments_validators.h"
 #include "third_party/blink/renderer/modules/service_worker/respond_with_observer.h"
-#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h"
+#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
+#include "third_party/blink/renderer/modules/service_worker/service_worker_window_client.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -139,8 +140,12 @@
   }
   context->ConsumeWindowInteraction();
 
-  ServiceWorkerGlobalScopeClient::From(context)->OpenWindowForPaymentHandler(
-      parsed_url_to_open, resolver);
+  To<ServiceWorkerGlobalScope>(context)
+      ->GetServiceWorkerHost()
+      ->OpenPaymentHandlerWindow(
+          parsed_url_to_open,
+          ServiceWorkerWindowClient::CreateResolveWindowClientCallback(
+              resolver));
   return promise;
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index e04dd2d..d8dabbbd 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -709,7 +709,10 @@
       closed_(false),
       has_data_channels_(false),
       sdp_semantics_(configuration.sdp_semantics),
-      sdp_semantics_specified_(sdp_semantics_specified) {
+      sdp_semantics_specified_(sdp_semantics_specified),
+      blink_webrtc_time_diff_(
+          base::TimeTicks::Now() - base::TimeTicks() -
+          base::TimeDelta::FromMicroseconds(rtc::TimeMicros())) {
   Document* document = To<Document>(GetExecutionContext());
 
   InstanceCounters::IncrementCounter(
@@ -3270,6 +3273,13 @@
   MediaStreamObserver::Trace(visitor);
 }
 
+base::TimeTicks RTCPeerConnection::WebRtcMsToBlinkTimeTicks(
+    double webrtc_monotonic_time_ms) const {
+  return base::TimeTicks() +
+         base::TimeDelta::FromMilliseconds(webrtc_monotonic_time_ms) +
+         blink_webrtc_time_diff_;
+}
+
 int RTCPeerConnection::PeerConnectionCount() {
   return InstanceCounters::CounterValue(
       InstanceCounters::kRTCPeerConnectionCounter);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
index 21dfdcf1..233b544e 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
@@ -348,6 +348,9 @@
 
   void Trace(blink::Visitor*) override;
 
+  base::TimeTicks WebRtcMsToBlinkTimeTicks(
+      double webrtc_monotonic_time_ms) const;
+
  private:
   FRIEND_TEST_ALL_PREFIXES(RTCPeerConnectionTest, GetAudioTrack);
   FRIEND_TEST_ALL_PREFIXES(RTCPeerConnectionTest, GetVideoTrack);
@@ -553,6 +556,9 @@
   webrtc::SdpSemantics sdp_semantics_;
   // Whether sdpSemantics was specified at construction.
   bool sdp_semantics_specified_;
+
+  // Blink and WebRTC timestamp diff.
+  const base::TimeDelta blink_webrtc_time_diff_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
index d47e9aa..7253af83 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/public/platform/web_media_stream.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_source.h"
+#include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_capabilities.h"
@@ -69,13 +70,21 @@
 HeapVector<Member<RTCRtpSynchronizationSource>>
 RTCRtpReceiver::getSynchronizationSources() {
   UpdateSourcesIfNeeded();
+
+  Document* document = To<Document>(pc_->GetExecutionContext());
+  DocumentLoadTiming& time_converter = document->Loader()->GetTiming();
+
   HeapVector<Member<RTCRtpSynchronizationSource>> synchronization_sources;
   for (const auto& web_source : web_sources_) {
     if (web_source->SourceType() != WebRTCRtpSource::Type::kSSRC)
       continue;
     RTCRtpSynchronizationSource* synchronization_source =
         MakeGarbageCollected<RTCRtpSynchronizationSource>();
-    synchronization_source->setTimestamp(web_source->TimestampMs());
+    synchronization_source->setTimestamp(
+        time_converter
+            .MonotonicTimeToPseudoWallTime(
+                pc_->WebRtcMsToBlinkTimeTicks(web_source->TimestampMs()))
+            .InMilliseconds());
     synchronization_source->setSource(web_source->Source());
     if (web_source->AudioLevel())
       synchronization_source->setAudioLevel(*web_source->AudioLevel());
@@ -88,13 +97,21 @@
 HeapVector<Member<RTCRtpContributingSource>>
 RTCRtpReceiver::getContributingSources() {
   UpdateSourcesIfNeeded();
+
+  Document* document = To<Document>(pc_->GetExecutionContext());
+  DocumentLoadTiming& time_converter = document->Loader()->GetTiming();
+
   HeapVector<Member<RTCRtpContributingSource>> contributing_sources;
   for (const auto& web_source : web_sources_) {
     if (web_source->SourceType() != WebRTCRtpSource::Type::kCSRC)
       continue;
     RTCRtpContributingSource* contributing_source =
         MakeGarbageCollected<RTCRtpContributingSource>();
-    contributing_source->setTimestamp(web_source->TimestampMs());
+    contributing_source->setTimestamp(
+        time_converter
+            .MonotonicTimeToPseudoWallTime(
+                pc_->WebRtcMsToBlinkTimeTicks(web_source->TimestampMs()))
+            .InMilliseconds());
     contributing_source->setSource(web_source->Source());
     if (web_source->AudioLevel())
       contributing_source->setAudioLevel(*web_source->AudioLevel());
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_client.cc b/third_party/blink/renderer/modules/service_worker/service_worker_client.cc
index 870189b..91fbf9b4 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_client.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_client.cc
@@ -14,8 +14,9 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/use_counter.h"
 #include "third_party/blink/renderer/core/messaging/blink_transferable_message.h"
+#include "third_party/blink/renderer/core/messaging/message_port.h"
 #include "third_party/blink/renderer/core/messaging/post_message_options.h"
-#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h"
+#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 
@@ -100,8 +101,9 @@
   if (exception_state.HadException())
     return;
 
-  ServiceWorkerGlobalScopeClient::From(context)->PostMessageToClient(
-      uuid_, std::move(msg));
+  To<ServiceWorkerGlobalScope>(context)
+      ->GetServiceWorkerHost()
+      ->PostMessageToClient(uuid_, std::move(msg));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_clients.cc b/third_party/blink/renderer/modules/service_worker/service_worker_clients.cc
index 144f7a5..f2c59e6 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_clients.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_clients.cc
@@ -14,10 +14,9 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worker_location.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_error.h"
-#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h"
+#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_window_client.h"
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
@@ -110,45 +109,47 @@
 
 ScriptPromise ServiceWorkerClients::get(ScriptState* script_state,
                                         const String& id) {
-  ExecutionContext* execution_context = ExecutionContext::From(script_state);
+  ServiceWorkerGlobalScope* global_scope =
+      To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
   // TODO(jungkees): May be null due to worker termination:
   // http://crbug.com/413518.
-  if (!execution_context)
+  if (!global_scope)
     return ScriptPromise();
 
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ServiceWorkerGlobalScopeClient::From(execution_context)
-      ->GetClient(id, WTF::Bind(&DidGetClient, WrapPersistent(resolver)));
+  global_scope->GetServiceWorkerHost()->GetClient(
+      id, WTF::Bind(&DidGetClient, WrapPersistent(resolver)));
   return resolver->Promise();
 }
 
 ScriptPromise ServiceWorkerClients::matchAll(
     ScriptState* script_state,
     const ClientQueryOptions* options) {
-  ExecutionContext* execution_context = ExecutionContext::From(script_state);
+  ServiceWorkerGlobalScope* global_scope =
+      To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
   // FIXME: May be null due to worker termination: http://crbug.com/413518.
-  if (!execution_context)
+  if (!global_scope)
     return ScriptPromise();
 
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ServiceWorkerGlobalScopeClient::From(execution_context)
-      ->GetClients(
-          mojom::blink::ServiceWorkerClientQueryOptions::New(
-              options->includeUncontrolled(), GetClientType(options->type())),
-          WTF::Bind(&DidGetClients, WrapPersistent(resolver)));
+  global_scope->GetServiceWorkerHost()->GetClients(
+      mojom::blink::ServiceWorkerClientQueryOptions::New(
+          options->includeUncontrolled(), GetClientType(options->type())),
+      WTF::Bind(&DidGetClients, WrapPersistent(resolver)));
   return resolver->Promise();
 }
 
 ScriptPromise ServiceWorkerClients::claim(ScriptState* script_state) {
-  ExecutionContext* execution_context = ExecutionContext::From(script_state);
+  ServiceWorkerGlobalScope* global_scope =
+      To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
 
   // FIXME: May be null due to worker termination: http://crbug.com/413518.
-  if (!execution_context)
+  if (!global_scope)
     return ScriptPromise();
 
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ServiceWorkerGlobalScopeClient::From(execution_context)
-      ->Claim(WTF::Bind(&DidClaim, WrapPersistent(resolver)));
+  global_scope->GetServiceWorkerHost()->ClaimClients(
+      WTF::Bind(&DidClaim, WrapPersistent(resolver)));
   return resolver->Promise();
 }
 
@@ -156,33 +157,34 @@
                                                const String& url) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
-  ExecutionContext* context = ExecutionContext::From(script_state);
+  ServiceWorkerGlobalScope* global_scope =
+      To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
 
-  KURL parsed_url =
-      KURL(To<WorkerGlobalScope>(context)->location()->Url(), url);
+  KURL parsed_url = KURL(global_scope->location()->Url(), url);
   if (!parsed_url.IsValid()) {
     resolver->Reject(V8ThrowException::CreateTypeError(
         script_state->GetIsolate(), "'" + url + "' is not a valid URL."));
     return promise;
   }
 
-  if (!context->GetSecurityOrigin()->CanDisplay(parsed_url)) {
+  if (!global_scope->GetSecurityOrigin()->CanDisplay(parsed_url)) {
     resolver->Reject(V8ThrowException::CreateTypeError(
         script_state->GetIsolate(),
         "'" + parsed_url.ElidedString() + "' cannot be opened."));
     return promise;
   }
 
-  if (!context->IsWindowInteractionAllowed()) {
+  if (!global_scope->IsWindowInteractionAllowed()) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kInvalidAccessError,
         "Not allowed to open a window."));
     return promise;
   }
-  context->ConsumeWindowInteraction();
+  global_scope->ConsumeWindowInteraction();
 
-  ServiceWorkerGlobalScopeClient::From(context)->OpenWindowForClients(
-      parsed_url, resolver);
+  global_scope->GetServiceWorkerHost()->OpenNewTab(
+      parsed_url,
+      ServiceWorkerWindowClient::CreateResolveWindowClientCallback(resolver));
   return promise;
 }
 
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index 260ef39..35c9eea 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -378,9 +378,8 @@
 
 void ServiceWorkerGlobalScope::Dispose() {
   DCHECK(IsContextThread());
-  ServiceWorkerGlobalScopeClient::From(GetExecutionContext())
-      ->WillDestroyWorkerContext();
   timeout_timer_.reset();
+  service_worker_host_.reset();
   binding_.Close();
   controller_.reset();
   WorkerGlobalScope::Dispose();
@@ -608,8 +607,8 @@
     return ScriptPromise();
 
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ServiceWorkerGlobalScopeClient::From(execution_context)
-      ->SkipWaiting(WTF::Bind(&DidSkipWaiting, WrapPersistent(resolver)));
+  GetServiceWorkerHost()->SkipWaiting(
+      WTF::Bind(&DidSkipWaiting, WrapPersistent(resolver)));
   return resolver->Promise();
 }
 
@@ -1249,6 +1248,12 @@
   return std::move(cache_storage_info_);
 }
 
+mojom::blink::ServiceWorkerHost*
+ServiceWorkerGlobalScope::GetServiceWorkerHost() {
+  DCHECK(service_worker_host_);
+  return service_worker_host_.get();
+}
+
 int ServiceWorkerGlobalScope::WillStartTask() {
   DCHECK(IsContextThread());
   DCHECK(timeout_timer_);
@@ -1348,9 +1353,10 @@
     mojom::blink::FetchHandlerExistence fetch_hander_existence) {
   DCHECK(IsContextThread());
 
-  ServiceWorkerGlobalScopeClient::From(GetExecutionContext())
-      ->BindServiceWorkerHost(std::move(service_worker_host),
-                              GetTaskRunner(TaskType::kInternalDefault));
+  DCHECK(service_worker_host.is_valid());
+  DCHECK(!service_worker_host_);
+  service_worker_host_.Bind(std::move(service_worker_host),
+                            GetTaskRunner(TaskType::kInternalDefault));
 
   // Set ServiceWorkerGlobalScope#registration.
   DCHECK_NE(registration_info->registration_id,
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
index e7db5abf..2a328ef 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
@@ -276,6 +276,8 @@
 
   mojom::blink::CacheStoragePtrInfo TakeCacheStorage();
 
+  mojom::blink::ServiceWorkerHost* GetServiceWorkerHost();
+
   // Called when a task is going to be scheduled on the service worker.
   // The service worker shouldn't request to be terminated until the task is
   // finished. Returns an id for the task. The caller must call DidEndTask()
@@ -436,6 +438,10 @@
   // ServiceWorkerGlobalScope#caches.
   mojom::blink::CacheStoragePtrInfo cache_storage_info_;
 
+  // Bound by the first Mojo call received on the service worker thread
+  // mojom::blink::ServiceWorker::InitializeGlobalScope().
+  mojom::blink::ServiceWorkerHostAssociatedPtr service_worker_host_;
+
   mojo::Binding<mojom::blink::ServiceWorker> binding_;
 
   // Maps for inflight event callbacks.
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.cc
index d5973a7c..8f7dffd 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.cc
@@ -32,128 +32,22 @@
 
 #include <memory>
 #include <utility>
-#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h"
 #include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/fetch/response.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
-#include "third_party/blink/renderer/modules/service_worker/service_worker_window_client.h"
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
-#include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
 namespace {
 
-void DidNavigateOrOpenWindow(ScriptPromiseResolver* resolver,
-                             bool success,
-                             mojom::blink::ServiceWorkerClientInfoPtr info,
-                             const String& error_msg) {
-  if (!resolver->GetExecutionContext() ||
-      resolver->GetExecutionContext()->IsContextDestroyed()) {
-    return;
-  }
-
-  if (!success) {
-    DCHECK(!info);
-    DCHECK(!error_msg.IsNull());
-    ScriptState::Scope scope(resolver->GetScriptState());
-    resolver->Reject(V8ThrowException::CreateTypeError(
-        resolver->GetScriptState()->GetIsolate(), error_msg));
-    return;
-  }
-  ServiceWorkerWindowClient* window_client = nullptr;
-  // Even if the open/navigation succeeded, |info| may be null if information of
-  // the opened/navigated window could not be obtained (this can happen for a
-  // cross-origin window, or if the browser process could not get the
-  // information in time before the window was closed).
-  if (info)
-    window_client = ServiceWorkerWindowClient::Create(*info);
-  resolver->Resolve(window_client);
-}
-
 }  // namespace
 
 ServiceWorkerGlobalScopeClient::ServiceWorkerGlobalScopeClient(
     WebServiceWorkerContextClient& client)
     : client_(client) {}
 
-void ServiceWorkerGlobalScopeClient::GetClient(const String& id,
-                                               GetClientCallback callback) {
-  service_worker_host_->GetClient(id, std::move(callback));
-}
-
-void ServiceWorkerGlobalScopeClient::GetClients(
-    mojom::blink::ServiceWorkerClientQueryOptionsPtr options,
-    GetClientsCallback callback) {
-  service_worker_host_->GetClients(std::move(options), std::move(callback));
-}
-
-void ServiceWorkerGlobalScopeClient::OpenWindowForClients(
-    const KURL& url,
-    ScriptPromiseResolver* resolver) {
-  service_worker_host_->OpenNewTab(
-      url, WTF::Bind(&DidNavigateOrOpenWindow, WrapPersistent(resolver)));
-}
-
-void ServiceWorkerGlobalScopeClient::OpenWindowForPaymentHandler(
-    const KURL& url,
-    ScriptPromiseResolver* resolver) {
-  service_worker_host_->OpenPaymentHandlerWindow(
-      url, WTF::Bind(&DidNavigateOrOpenWindow, WrapPersistent(resolver)));
-}
-
-void ServiceWorkerGlobalScopeClient::SetCachedMetadata(const KURL& url,
-                                                       const uint8_t* data,
-                                                       size_t size) {
-  Vector<uint8_t> meta_data;
-  meta_data.Append(data, SafeCast<wtf_size_t>(size));
-  service_worker_host_->SetCachedMetadata(url, meta_data);
-}
-
-void ServiceWorkerGlobalScopeClient::ClearCachedMetadata(const KURL& url) {
-  service_worker_host_->ClearCachedMetadata(url);
-}
-
-void ServiceWorkerGlobalScopeClient::PostMessageToClient(
-    const String& client_uuid,
-    BlinkTransferableMessage message) {
-  service_worker_host_->PostMessageToClient(client_uuid, std::move(message));
-}
-
-void ServiceWorkerGlobalScopeClient::SkipWaiting(SkipWaitingCallback callback) {
-  service_worker_host_->SkipWaiting(std::move(callback));
-}
-
-void ServiceWorkerGlobalScopeClient::Claim(ClaimCallback callback) {
-  service_worker_host_->ClaimClients(std::move(callback));
-}
-
-void ServiceWorkerGlobalScopeClient::Focus(const String& client_uuid,
-                                           FocusCallback callback) {
-  service_worker_host_->FocusClient(client_uuid, std::move(callback));
-}
-
-void ServiceWorkerGlobalScopeClient::Navigate(const String& client_uuid,
-                                              const KURL& url,
-                                              ScriptPromiseResolver* resolver) {
-  service_worker_host_->NavigateClient(
-      client_uuid, url,
-      WTF::Bind(&DidNavigateOrOpenWindow, WrapPersistent(resolver)));
-}
-
-void ServiceWorkerGlobalScopeClient::BindServiceWorkerHost(
-    mojom::blink::ServiceWorkerHostAssociatedPtrInfo service_worker_host,
-    scoped_refptr<base::SequencedTaskRunner> task_runner) {
-  DCHECK(service_worker_host.is_valid());
-  DCHECK(!service_worker_host_);
-  service_worker_host_.Bind(std::move(service_worker_host),
-                            std::move(task_runner));
-}
-
 void ServiceWorkerGlobalScopeClient::SetupNavigationPreload(
     int fetch_event_id,
     const KURL& url,
@@ -171,10 +65,6 @@
   client_.RequestTermination(std::move(callback));
 }
 
-void ServiceWorkerGlobalScopeClient::WillDestroyWorkerContext() {
-  service_worker_host_.reset();
-}
-
 const char ServiceWorkerGlobalScopeClient::kSupplementName[] =
     "ServiceWorkerGlobalScopeClient";
 
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h
index 9dfa269..fe02258e 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h
@@ -34,21 +34,15 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "third_party/blink/public/common/messaging/transferable_message.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker.mojom-blink.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom-blink.h"
-#include "third_party/blink/renderer/core/messaging/message_port.h"
+#include "third_party/blink/public/mojom/service_worker/dispatch_fetch_event_params.mojom-blink.h"
 #include "third_party/blink/renderer/core/workers/worker_clients.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
 class ExecutionContext;
 class KURL;
-class ScriptPromiseResolver;
 class WebServiceWorkerContextClient;
-class WorkerClients;
 
 class MODULES_EXPORT ServiceWorkerGlobalScopeClient final
     : public GarbageCollectedFinalized<ServiceWorkerGlobalScopeClient>,
@@ -56,44 +50,17 @@
   USING_GARBAGE_COLLECTED_MIXIN(ServiceWorkerGlobalScopeClient);
 
  public:
-  using ClaimCallback = mojom::blink::ServiceWorkerHost::ClaimClientsCallback;
-  using SkipWaitingCallback =
-      mojom::blink::ServiceWorkerHost::SkipWaitingCallback;
-  using GetClientCallback = mojom::blink::ServiceWorkerHost::GetClientCallback;
-  using GetClientsCallback =
-      mojom::blink::ServiceWorkerHost::GetClientsCallback;
-  using FocusCallback = mojom::blink::ServiceWorkerHost::FocusClientCallback;
-
   static const char kSupplementName[];
 
   explicit ServiceWorkerGlobalScopeClient(WebServiceWorkerContextClient&);
 
-  // Called from ServiceWorkerClients.
-  void GetClient(const String&, GetClientCallback);
-  void GetClients(mojom::blink::ServiceWorkerClientQueryOptionsPtr,
-                  GetClientsCallback);
-  void OpenWindowForClients(const KURL&, ScriptPromiseResolver*);
-  void OpenWindowForPaymentHandler(const KURL&, ScriptPromiseResolver*);
-  void SetCachedMetadata(const KURL&, const uint8_t*, size_t);
-  void ClearCachedMetadata(const KURL&);
-  void PostMessageToClient(const String& client_uuid, BlinkTransferableMessage);
-  void SkipWaiting(SkipWaitingCallback);
-  void Claim(ClaimCallback);
-  void Focus(const String& client_uuid, FocusCallback);
-  void Navigate(const String& client_uuid, const KURL&, ScriptPromiseResolver*);
-
   // Called from ServiceWorkerGlobalScope.
-  void BindServiceWorkerHost(
-      mojom::blink::ServiceWorkerHostAssociatedPtrInfo service_worker_host,
-      scoped_refptr<base::SequencedTaskRunner> task_runner);
   void SetupNavigationPreload(
       int fetch_event_id,
       const KURL& url,
       mojom::blink::FetchEventPreloadHandlePtr preload_handle);
   void RequestTermination(base::OnceCallback<void(bool)> callback);
 
-  void WillDestroyWorkerContext();
-
   static ServiceWorkerGlobalScopeClient* From(ExecutionContext*);
 
   void Trace(blink::Visitor*) override;
@@ -101,12 +68,6 @@
  private:
   WebServiceWorkerContextClient& client_;
 
-  // Lives on the service worker thread, is bound by BindServiceWorkerHost()
-  // which is triggered by the first Mojo call received on the service worker
-  // thread mojom::blink::ServiceWorker::InitializeGlobalScope(), and is closed
-  // by WillDestroyWorkerContext().
-  mojom::blink::ServiceWorkerHostAssociatedPtr service_worker_host_;
-
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerGlobalScopeClient);
 };
 
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_script_cached_metadata_handler.cc b/third_party/blink/renderer/modules/service_worker/service_worker_script_cached_metadata_handler.cc
index cc88766..8a1f3e8 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_script_cached_metadata_handler.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_script_cached_metadata_handler.cc
@@ -4,8 +4,7 @@
 
 #include "third_party/blink/renderer/modules/service_worker/service_worker_script_cached_metadata_handler.h"
 
-#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
-#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h"
+#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
 #include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource.h"
 
@@ -13,10 +12,10 @@
 
 ServiceWorkerScriptCachedMetadataHandler::
     ServiceWorkerScriptCachedMetadataHandler(
-        WorkerGlobalScope* worker_global_scope,
+        ServiceWorkerGlobalScope* global_scope,
         const KURL& script_url,
         std::unique_ptr<Vector<uint8_t>> meta_data)
-    : worker_global_scope_(worker_global_scope), script_url_(script_url) {
+    : global_scope_(global_scope), script_url_(script_url) {
   if (meta_data) {
     cached_metadata_ =
         CachedMetadata::CreateFromSerializedData(std::move(*meta_data));
@@ -27,7 +26,7 @@
     ~ServiceWorkerScriptCachedMetadataHandler() = default;
 
 void ServiceWorkerScriptCachedMetadataHandler::Trace(blink::Visitor* visitor) {
-  visitor->Trace(worker_global_scope_);
+  visitor->Trace(global_scope_);
   CachedMetadataHandler::Trace(visitor);
 }
 
@@ -40,9 +39,8 @@
     return;
   cached_metadata_ = CachedMetadata::Create(data_type_id, data, size);
   const Vector<uint8_t>& serialized_data = cached_metadata_->SerializedData();
-  ServiceWorkerGlobalScopeClient::From(worker_global_scope_)
-      ->SetCachedMetadata(script_url_, serialized_data.data(),
-                          serialized_data.size());
+  global_scope_->GetServiceWorkerHost()->SetCachedMetadata(script_url_,
+                                                           serialized_data);
 }
 
 void ServiceWorkerScriptCachedMetadataHandler::ClearCachedMetadata(
@@ -50,8 +48,7 @@
   if (type != kSendToPlatform)
     return;
   cached_metadata_ = nullptr;
-  ServiceWorkerGlobalScopeClient::From(worker_global_scope_)
-      ->ClearCachedMetadata(script_url_);
+  global_scope_->GetServiceWorkerHost()->ClearCachedMetadata(script_url_);
 }
 
 scoped_refptr<CachedMetadata>
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_script_cached_metadata_handler.h b/third_party/blink/renderer/modules/service_worker/service_worker_script_cached_metadata_handler.h
index 46a52fa0..fb16653 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_script_cached_metadata_handler.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_script_cached_metadata_handler.h
@@ -13,22 +13,22 @@
 
 namespace blink {
 
-class WorkerGlobalScope;
 class CachedMetadata;
+class ServiceWorkerGlobalScope;
 
 class ServiceWorkerScriptCachedMetadataHandler
     : public SingleCachedMetadataHandler {
  public:
   static ServiceWorkerScriptCachedMetadataHandler* Create(
-      WorkerGlobalScope* worker_global_scope,
+      ServiceWorkerGlobalScope* global_scope,
       const KURL& script_url,
       std::unique_ptr<Vector<uint8_t>> meta_data) {
     return MakeGarbageCollected<ServiceWorkerScriptCachedMetadataHandler>(
-        worker_global_scope, script_url, std::move(meta_data));
+        global_scope, script_url, std::move(meta_data));
   }
 
   ServiceWorkerScriptCachedMetadataHandler(
-      WorkerGlobalScope*,
+      ServiceWorkerGlobalScope*,
       const KURL& script_url,
       std::unique_ptr<Vector<uint8_t>> meta_data);
   ~ServiceWorkerScriptCachedMetadataHandler() override;
@@ -47,7 +47,7 @@
   size_t GetCodeCacheSize() const override;
 
  private:
-  Member<WorkerGlobalScope> worker_global_scope_;
+  Member<ServiceWorkerGlobalScope> global_scope_;
   KURL script_url_;
   scoped_refptr<CachedMetadata> cached_metadata_;
 };
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_window_client.cc b/third_party/blink/renderer/modules/service_worker/service_worker_window_client.cc
index a79d6d5..620b73c 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_window_client.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_window_client.cc
@@ -10,11 +10,11 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/messaging/message_port.h"
 #include "third_party/blink/renderer/core/page/page_hidden_state.h"
-#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worker_location.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_error.h"
-#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_client.h"
+#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 
@@ -38,6 +38,33 @@
   resolver->Resolve(ServiceWorkerWindowClient::Create(*client));
 }
 
+void DidNavigateOrOpenWindow(ScriptPromiseResolver* resolver,
+                             bool success,
+                             mojom::blink::ServiceWorkerClientInfoPtr info,
+                             const String& error_msg) {
+  if (!resolver->GetExecutionContext() ||
+      resolver->GetExecutionContext()->IsContextDestroyed()) {
+    return;
+  }
+
+  if (!success) {
+    DCHECK(!info);
+    DCHECK(!error_msg.IsNull());
+    ScriptState::Scope scope(resolver->GetScriptState());
+    resolver->Reject(V8ThrowException::CreateTypeError(
+        resolver->GetScriptState()->GetIsolate(), error_msg));
+    return;
+  }
+  ServiceWorkerWindowClient* window_client = nullptr;
+  // Even if the open/navigation succeeded, |info| may be null if information of
+  // the opened/navigated window could not be obtained (this can happen for a
+  // cross-origin window, or if the browser process could not get the
+  // information in time before the window was closed).
+  if (info)
+    window_client = MakeGarbageCollected<ServiceWorkerWindowClient>(*info);
+  resolver->Resolve(window_client);
+}
+
 }  // namespace
 
 ServiceWorkerWindowClient* ServiceWorkerWindowClient::Create(
@@ -46,6 +73,13 @@
   return MakeGarbageCollected<ServiceWorkerWindowClient>(info);
 }
 
+// static
+ServiceWorkerWindowClient::ResolveWindowClientCallback
+ServiceWorkerWindowClient::CreateResolveWindowClientCallback(
+    ScriptPromiseResolver* resolver) {
+  return WTF::Bind(&DidNavigateOrOpenWindow, WrapPersistent(resolver));
+}
+
 ServiceWorkerWindowClient::ServiceWorkerWindowClient(
     const mojom::blink::ServiceWorkerClientInfo& info)
     : ServiceWorkerClient(info),
@@ -61,17 +95,19 @@
 ScriptPromise ServiceWorkerWindowClient::focus(ScriptState* script_state) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
+  ServiceWorkerGlobalScope* global_scope =
+      To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
 
-  if (!ExecutionContext::From(script_state)->IsWindowInteractionAllowed()) {
+  if (!global_scope->IsWindowInteractionAllowed()) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kInvalidAccessError,
         "Not allowed to focus a window."));
     return promise;
   }
-  ExecutionContext::From(script_state)->ConsumeWindowInteraction();
+  global_scope->ConsumeWindowInteraction();
 
-  ServiceWorkerGlobalScopeClient::From(ExecutionContext::From(script_state))
-      ->Focus(Uuid(), WTF::Bind(&DidFocus, WrapPersistent(resolver)));
+  global_scope->GetServiceWorkerHost()->FocusClient(
+      Uuid(), WTF::Bind(&DidFocus, WrapPersistent(resolver)));
   return promise;
 }
 
@@ -79,24 +115,24 @@
                                                   const String& url) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
-  ExecutionContext* context = ExecutionContext::From(script_state);
+  ServiceWorkerGlobalScope* global_scope =
+      To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
 
-  KURL parsed_url =
-      KURL(To<WorkerGlobalScope>(context)->location()->Url(), url);
+  KURL parsed_url = KURL(global_scope->location()->Url(), url);
   if (!parsed_url.IsValid() || parsed_url.ProtocolIsAbout()) {
     resolver->Reject(V8ThrowException::CreateTypeError(
         script_state->GetIsolate(), "'" + url + "' is not a valid URL."));
     return promise;
   }
-  if (!context->GetSecurityOrigin()->CanDisplay(parsed_url)) {
+  if (!global_scope->GetSecurityOrigin()->CanDisplay(parsed_url)) {
     resolver->Reject(V8ThrowException::CreateTypeError(
         script_state->GetIsolate(),
         "'" + parsed_url.ElidedString() + "' cannot navigate."));
     return promise;
   }
 
-  ServiceWorkerGlobalScopeClient::From(context)->Navigate(Uuid(), parsed_url,
-                                                          resolver);
+  global_scope->GetServiceWorkerHost()->NavigateClient(
+      Uuid(), parsed_url, CreateResolveWindowClientCallback(resolver));
   return promise;
 }
 
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_window_client.h b/third_party/blink/renderer/modules/service_worker/service_worker_window_client.h
index 1cb25b9..c0c602d 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_window_client.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_window_client.h
@@ -14,6 +14,7 @@
 
 namespace blink {
 
+class ScriptPromiseResolver;
 class ScriptState;
 
 class MODULES_EXPORT ServiceWorkerWindowClient final
@@ -21,9 +22,15 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
+  using ResolveWindowClientCallback = base::OnceCallback<
+      void(bool, mojom::blink::ServiceWorkerClientInfoPtr, const String&)>;
+
   static ServiceWorkerWindowClient* Create(
       const mojom::blink::ServiceWorkerClientInfo&);
 
+  static ResolveWindowClientCallback CreateResolveWindowClientCallback(
+      ScriptPromiseResolver*);
+
   explicit ServiceWorkerWindowClient(
       const mojom::blink::ServiceWorkerClientInfo&);
   ~ServiceWorkerWindowClient() override;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_video_texture.idl b/third_party/blink/renderer/modules/webgl/webgl_video_texture.idl
index 998d2b8..e08dffe 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_video_texture.idl
+++ b/third_party/blink/renderer/modules/webgl/webgl_video_texture.idl
@@ -4,8 +4,6 @@
 
 // https://www.khronos.org/registry/webgl/extensions/proposals/WEBGL_video_texture/
 
-typedef unsigned long GLenum;
-
 [
     RuntimeEnabled=WebGLDraftExtensions,
     DoNotCheckConstants,
@@ -15,4 +13,4 @@
     const GLenum SAMPLER_VIDEO_IMAGE = 0x9249;
 
     [CallWith=ExecutionContext, RaisesException] WebGLVideoFrameInfo VideoElementTargetVideoTexture(GLenum target, HTMLVideoElement video);
-};
\ No newline at end of file
+};
diff --git a/third_party/blink/renderer/platform/DEPS b/third_party/blink/renderer/platform/DEPS
index c1d52350..3774affa 100644
--- a/third_party/blink/renderer/platform/DEPS
+++ b/third_party/blink/renderer/platform/DEPS
@@ -10,7 +10,6 @@
     "+base/cpu.h",
     "+base/feature_list.h",
     "+base/files",
-    "+base/containers/adapters.h",
     "+base/containers/flat_map.h",
     "+base/guid.h",
     "+base/json",
diff --git a/third_party/blink/renderer/platform/exported/mediastream/webrtc_uma_histograms.cc b/third_party/blink/renderer/platform/exported/mediastream/webrtc_uma_histograms.cc
index 32820de4..2c0817e1 100644
--- a/third_party/blink/renderer/platform/exported/mediastream/webrtc_uma_histograms.cc
+++ b/third_party/blink/renderer/platform/exported/mediastream/webrtc_uma_histograms.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/public/platform/modules/mediastream/webrtc_uma_histograms.h"
 
 #include "base/metrics/histogram_macros.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
 
 namespace blink {
 
@@ -13,9 +14,10 @@
                             NUM_MEDIA_STREAM_REQUEST_WITH_NO_RESULT);
 }
 
-void LogUserMediaRequestResult(MediaStreamRequestResult result) {
-  UMA_HISTOGRAM_ENUMERATION("WebRTC.UserMediaRequest.Result2", result,
-                            NUM_MEDIA_REQUEST_RESULTS);
+void LogUserMediaRequestResult(mojom::blink::MediaStreamRequestResult result) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "WebRTC.UserMediaRequest.Result2", result,
+      mojom::blink::MediaStreamRequestResult::NUM_MEDIA_REQUEST_RESULTS);
 }
 
 void UpdateWebRTCMethodCount(WebRTCAPIName api_name) {
diff --git a/third_party/blink/web_tests/ASANExpectations b/third_party/blink/web_tests/ASANExpectations
index 9762aef..7973887 100644
--- a/third_party/blink/web_tests/ASANExpectations
+++ b/third_party/blink/web_tests/ASANExpectations
@@ -67,16 +67,16 @@
 crbug.com/803276 inspector-protocol/memory/sampling-native-snapshot.js [ Skip ]
 
 # CORS test crash on ASAN
-crbug.com/838057 [ Linux ] virtual/outofblink-cors/external/wpt/fetch/api/cors/cors-cookies-redirect.any.html [ Crash ]
-crbug.com/838057 [ Linux ] virtual/outofblink-cors/external/wpt/fetch/api/cors/cors-cookies-redirect.any.worker.html [ Crash ]
-crbug.com/838057 [ Linux ] virtual/outofblink-cors/external/wpt/fetch/api/cors/cors-preflight-star.any.html [ Crash ]
-crbug.com/838057 [ Linux ] virtual/outofblink-cors/external/wpt/fetch/api/cors/cors-preflight-star.any.worker.html [ Crash ]
-crbug.com/838057 [ Linux ] virtual/outofblink-cors/external/wpt/fetch/api/cors/cors-preflight.any.html [ Crash ]
-crbug.com/838057 [ Linux ] virtual/outofblink-cors/external/wpt/fetch/api/cors/cors-preflight.any.worker.html [ Crash ]
-crbug.com/838057 [ Linux ] virtual/outofblink-cors/external/wpt/fetch/api/cors/cors-redirect-preflight.any.html [ Crash ]
-crbug.com/838057 [ Linux ] virtual/outofblink-cors/external/wpt/fetch/api/cors/cors-redirect-preflight.any.worker.html [ Crash ]
-crbug.com/838057 [ Linux ] virtual/outofblink-cors/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video-cache.https.html [ Crash ]
-crbug.com/838057 [ Linux ] virtual/outofblink-cors/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video.https.html [ Crash ]
+crbug.com/838057 [ Linux ] virtual/blink-cors/external/wpt/fetch/api/cors/cors-cookies-redirect.any.html [ Crash ]
+crbug.com/838057 [ Linux ] virtual/blink-cors/external/wpt/fetch/api/cors/cors-cookies-redirect.any.worker.html [ Crash ]
+crbug.com/838057 [ Linux ] virtual/blink-cors/external/wpt/fetch/api/cors/cors-preflight-star.any.html [ Crash ]
+crbug.com/838057 [ Linux ] virtual/blink-cors/external/wpt/fetch/api/cors/cors-preflight-star.any.worker.html [ Crash ]
+crbug.com/838057 [ Linux ] virtual/blink-cors/external/wpt/fetch/api/cors/cors-preflight.any.html [ Crash ]
+crbug.com/838057 [ Linux ] virtual/blink-cors/external/wpt/fetch/api/cors/cors-preflight.any.worker.html [ Crash ]
+crbug.com/838057 [ Linux ] virtual/blink-cors/external/wpt/fetch/api/cors/cors-redirect-preflight.any.html [ Crash ]
+crbug.com/838057 [ Linux ] virtual/blink-cors/external/wpt/fetch/api/cors/cors-redirect-preflight.any.worker.html [ Crash ]
+crbug.com/838057 [ Linux ] virtual/blink-cors/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video-cache.https.html [ Crash ]
+crbug.com/838057 [ Linux ] virtual/blink-cors/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video.https.html [ Crash ]
 
 # Disabled by sheriff due to test crash
 crbug.com/896068 [ Linux ] webaudio/AudioBuffer/huge-buffer.html [ Crash ]
diff --git a/third_party/blink/web_tests/MSANExpectations b/third_party/blink/web_tests/MSANExpectations
index 8618df11..36271445 100644
--- a/third_party/blink/web_tests/MSANExpectations
+++ b/third_party/blink/web_tests/MSANExpectations
@@ -158,25 +158,25 @@
 crbug.com/856601 [ Linux ] external/wpt/webstorage/idlharness.window.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] virtual/feature-policy-permissions/external/wpt/mediacapture-streams/MediaDevices-IDL-enumerateDevices.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] virtual/feature-policy-permissions/external/wpt/mediacapture-streams/idlharness.https.window.html [ Pass Timeout ]
-crbug.com/856601 [ Linux ] virtual/outofblink-cors/external/wpt/fetch/cors-rfc1918/idlharness.tentative.https.any.serviceworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] virtual/blink-cors/external/wpt/fetch/cors-rfc1918/idlharness.tentative.https.any.serviceworker.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/interfaces-sw.https.html [ Timeout Pass ]
-crbug.com/856601 [ Linux ] virtual/outofblink-cors/external/wpt/service-workers/service-worker/interfaces-sw.https.html [ Timeout Pass ]
+crbug.com/856601 [ Linux ] virtual/blink-cors/external/wpt/service-workers/service-worker/interfaces-sw.https.html [ Timeout Pass ]
 crbug.com/856601 [ Linux ] virtual/service-worker-servicification/external/wpt/service-workers/service-worker/interfaces-sw.https.html [ Timeout Pass ]
 crbug.com/856601 [ Linux ] external/wpt/fetch/api/idl.any.sharedworker.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/fetch/cors-rfc1918/idlharness.tentative.https.any.serviceworker.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/media-capabilities/idlharness.any.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/orientation-event/idlharness.window.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/pointerlock/interfaces.window.html [ Pass Timeout ]
-crbug.com/856601 [ Linux ] virtual/outofblink-cors/external/wpt/xhr/idlharness.any.sharedworker.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] virtual/blink-cors/external/wpt/xhr/idlharness.any.sharedworker.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/bluetooth/idl/idlharness.tentative.https.window.html [ Timeout Pass ]
 crbug.com/856601 [ Linux ] external/wpt/css/css-pseudo/idlharness.html [ Timeout Pass ]
 crbug.com/856601 [ Linux ] external/wpt/hr-time/idlharness.any.worker.html [ Timeout Pass ]
 crbug.com/856601 [ Linux ] external/wpt/media-source/idlharness.any.html [ Timeout Pass ]
 crbug.com/856601 [ Linux ] external/wpt/page-visibility/idlharness.window.html [ Timeout Pass ]
 crbug.com/856601 [ Linux ] virtual/new-remote-playback-pipeline/external/wpt/remote-playback/idlharness.window.html [ Timeout Pass ]
-crbug.com/856601 [ Linux ] virtual/outofblink-cors/external/wpt/fetch/api/idl.any.html [ Timeout Pass ]
-crbug.com/856601 [ Linux ] virtual/outofblink-cors/external/wpt/fetch/cors-rfc1918/idlharness.tentative.any.html [ Timeout Pass ]
-crbug.com/856601 [ Linux ] virtual/outofblink-cors/external/wpt/xhr/idlharness.any.html [ Timeout Pass ]
+crbug.com/856601 [ Linux ] virtual/blink-cors/external/wpt/fetch/api/idl.any.html [ Timeout Pass ]
+crbug.com/856601 [ Linux ] virtual/blink-cors/external/wpt/fetch/cors-rfc1918/idlharness.tentative.any.html [ Timeout Pass ]
+crbug.com/856601 [ Linux ] virtual/blink-cors/external/wpt/xhr/idlharness.any.html [ Timeout Pass ]
 crbug.com/856601 [ Linux ] virtual/service-worker-servicification/external/wpt/service-workers/service-worker/interfaces-window.https.html [ Timeout Pass ]
 crbug.com/856601 [ Linux ] virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window.html [ Timeout Pass ]
 crbug.com/856601 [ Linux ] external/wpt/css/filter-effects/interfaces.any.html [ Timeout Pass ]
@@ -213,7 +213,7 @@
 crbug.com/914900 [ Linux ] external/wpt/fetch/api/idl.any.worker.html [ Pass Timeout ]
 crbug.com/914900 [ Linux ] external/wpt/secure-contexts/idlharness.any.worker.html [ Pass Timeout ]
 crbug.com/914900 [ Linux ] http/tests/devtools/network/preview-searchable.js [ Pass Timeout ]
-crbug.com/914900 [ Linux ] virtual/outofblink-cors/external/wpt/xhr/idlharness.any.worker.html [ Pass Timeout ]
+crbug.com/914900 [ Linux ] virtual/blink-cors/external/wpt/xhr/idlharness.any.worker.html [ Pass Timeout ]
 
 # Sheriff 2019-01-28
 crbug.com/925600 [ Linux ] external/wpt/webrtc-quic/RTCQuicStream.https.html [ Pass Timeout ]
@@ -244,4 +244,4 @@
 # Sheriff 2019-05-21
 crbug.com/856601 [ Linux ] external/wpt/notifications/idlharness.https.window.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/fetch/cors-rfc1918/idlharness.tentative.any.serviceworker.html [ Pass Timeout ]
-crbug.com/856601 [ Linux ] external/wpt/notifications/idlharness.https.any.sharedworker.html [ Pass Timeout ]
\ No newline at end of file
+crbug.com/856601 [ Linux ] external/wpt/notifications/idlharness.https.any.sharedworker.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index a7cee3ca..10c83ef 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -2162,6 +2162,11 @@
 external/wpt/webstorage/storage_session-manual.html [ WontFix ]
 external/wpt/xhr/send-authentication-existing-session-manual.htm [ WontFix ]
 external/wpt/xhr/send-authentication-prompt-2-manual.htm [ WontFix ]
+virtual/blink-cors/external/wpt/service-workers/service-worker/fetch-event-is-history-backward-navigation-manual.https.html [ WontFix ]
+virtual/blink-cors/external/wpt/service-workers/service-worker/fetch-event-is-history-forward-navigation-manual.https.html [ WontFix ]
+virtual/blink-cors/external/wpt/service-workers/service-worker/fetch-event-is-reload-navigation-manual.https.html [ WontFix ]
+virtual/blink-cors/external/wpt/xhr/send-authentication-existing-session-manual.htm [ WontFix ]
+virtual/blink-cors/external/wpt/xhr/send-authentication-prompt-2-manual.htm [ WontFix ]
 virtual/feature-policy-permissions/external/wpt/mediacapture-streams/MediaStream-MediaElement-preload-none-manual.https.html [ WontFix ]
 virtual/feature-policy-permissions/external/wpt/mediacapture-streams/MediaStreamTrack-end-manual.https.html [ WontFix ]
 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/fetch-event-is-history-backward-navigation-manual.https.html [ WontFix ]
@@ -2231,6 +2236,7 @@
 # https://foo.example.com/path?token=secret needs to be replaced with just
 # https://foo.example.com).
 crbug.com/669083 http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ WontFix ]
+crbug.com/669083 virtual/blink-cors/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ WontFix ]
 # --------------------------- code cache isolation -----------------------
 # Code cache isolation tests only make sense if either
 # 1) site isolation is disabled or
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 47f8e8d..b6f6746 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -151,6 +151,7 @@
 
 crbug.com/522646 http/tests/media/encrypted-media/encrypted-media-encrypted-event-different-origin.html [ Slow ]
 crbug.com/411164 [ Win ] http/tests/security/powerfulFeatureRestrictions/serviceworker-on-insecure-origin.html [ Slow ]
+crbug.com/411164 [ Win ] virtual/blink-cors/http/tests/security/powerfulFeatureRestrictions/serviceworker-on-insecure-origin.html [ Slow ]
 crbug.com/510337 http/tests/devtools/console/console-format.js [ Slow ]
 crbug.com/357427 http/tests/workers/terminate-during-sync-operation-file.html [ Slow ]
 crbug.com/357427 http/tests/workers/terminate-during-sync-operation-filesystem.html [ Slow ]
@@ -172,6 +173,7 @@
 crbug.com/372424 http/tests/serviceworker/registration-stress.html [ Slow ]
 crbug.com/448670 http/tests/serviceworker/register-different-script-many-times.html [ Slow ]
 crbug.com/516319 [ Win ] http/tests/fetch/ [ Slow ]
+crbug.com/516319 [ Win ] virtual/blink-cors/http/tests/fetch/ [ Slow ]
 crbug.com/516319 [ Win ] virtual/streaming-preload/http/tests/fetch/ [ Slow ]
 
 # Most crypto/subtle tests are slow some or most of the time.
@@ -256,6 +258,8 @@
 # These tests take 90 seconds on MSAN due to a large amount of JS execution.
 crbug.com/853977 [ Linux ] http/tests/fetch/chromium/call-extra-crash-tee.html [ Slow ]
 crbug.com/853977 [ Linux ] http/tests/fetch/chromium/release-handle-crash.html [ Slow ]
+crbug.com/853977 [ Linux ] virtual/blink-cors/http/tests/fetch/chromium/call-extra-crash-tee.html [ Slow ]
+crbug.com/853977 [ Linux ] virtual/blink-cors/http/tests/fetch/chromium/release-handle-crash.html [ Slow ]
 crbug.com/853977 [ Linux ] virtual/streaming-preload/http/tests/fetch/chromium/call-extra-crash-tee.html [ Slow ]
 crbug.com/853977 [ Linux ] virtual/streaming-preload/http/tests/fetch/chromium/release-handle-crash.html [ Slow ]
 crbug.com/853977 [ Linux ] virtual/streams-native/http/tests/fetch/chromium/call-extra-crash-tee.html [ Slow ]
@@ -630,3 +634,34 @@
 crbug.com/962831 [ Release ] http/tests/devtools/network/preview-searchable.js [ Slow ]
 
 crbug.com/967526 http/tests/devtools/console/console-uncaught-promise.js [ Slow ]
+
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/serviceworker/body-mixin-base-https-other-https.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/serviceworker/body-mixin.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/serviceworker/stream-reader-base-https-other-https.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/serviceworker/stream-reader.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/serviceworker/thorough/ [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/serviceworker-proxied/thorough/ [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/window/body-mixin-base-https-other-https.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/window/body-mixin.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/window/stream-reader-base-https-other-https.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/window/stream-reader.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/window/thorough/ [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/workers/body-mixin-base-https-other-https.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/workers/body-mixin.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/workers/stream-reader-base-https-other-https.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/workers/stream-reader.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/fetch/workers/thorough/ [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/navigation/navigation-interrupted-by-fragment.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/navigation/new-window-redirect-history.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/navigation/slowmetaredirect-basic.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/navigation/slowtimerredirect-basic.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/security/contentSecurityPolicy/redirect-with-delay.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/security/cross-frame-mouse-source-capabilities.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/security/frameNavigation/xss-ALLOWED-same-origin-top-navigation-without-user-gesture.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/security/video-poster-cross-origin-crash2.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-aborted.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-overridesexpires.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-simple.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-twice.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-worker-overridesexpires.html [ Slow ]
+crbug.com/874695 virtual/blink-cors/http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-worker-twice.html [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index b188e6551..3dd4c25 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -63,6 +63,7 @@
 crbug.com/645641 external/wpt/html/syntax/parsing/html5lib_tests19.html [ Crash Failure ]
 crbug.com/665058 http/tests/local/drag-over-remote-content.html [ Crash ]
 crbug.com/771003 http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
+crbug.com/771003 virtual/blink-cors/http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
 
 # Tests temporarily disabled with Site Isolation - known differences in product
 # behavior (either accepted for the long-term, or for the short-term):
@@ -592,6 +593,7 @@
 crbug.com/882385 virtual/layout_ng_experimental/external/wpt/css/css-contain/quote-scoping-002.html [ Failure ]
 crbug.com/882385 virtual/layout_ng_experimental/external/wpt/css/css-contain/quote-scoping-003.html [ Failure ]
 crbug.com/882385 virtual/layout_ng_experimental/external/wpt/css/css-contain/quote-scoping-004.html [ Failure ]
+crbug.com/881057 virtual/layout_ng_experimental/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-002.html [ Failure ]
 crbug.com/881057 [ Mac ] virtual/layout_ng_experimental/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-paint-clip-003.html [ Failure ]
 crbug.com/881057 [ Mac ] virtual/layout_ng_experimental/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-paint-clip-004.html [ Failure ]
 crbug.com/881057 [ Mac ] virtual/layout_ng_experimental/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-paint-clip-005.html [ Failure ]
@@ -1743,8 +1745,10 @@
 crbug.com/410974 virtual/threaded/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html [ Pass Failure ]
 
 crbug.com/390452 http/tests/security/isolatedWorld/media-query-wrapper-leaks.html [ Failure Pass Timeout ]
+crbug.com/390452 virtual/blink-cors/http/tests/security/isolatedWorld/media-query-wrapper-leaks.html [ Failure Pass Timeout ]
 crbug.com/390452 virtual/isolated_world_csp/http/tests/security/isolatedWorld/media-query-wrapper-leaks.html [ Failure Pass Timeout ]
 crbug.com/518987 http/tests/xmlhttprequest/navigation-abort-detaches-frame.html [ Pass Timeout ]
+crbug.com/518987 virtual/blink-cors/http/tests/xmlhttprequest/navigation-abort-detaches-frame.html [ Pass Timeout ]
 
 # These performance-sensitive user-timing tests are flaky in debug on all platforms, and flaky on all configurations of windows.
 # See: crbug.com/567965, crbug.com/518992, and crbug.com/518993
@@ -2169,6 +2173,10 @@
 crbug.com/528062 [ Win ] http/tests/security/contentSecurityPolicy/cached-frame-csp.html [ Failure ]
 crbug.com/528062 [ Win ] http/tests/security/xssAuditor/cached-frame.html [ Failure ]
 crbug.com/528062 [ Win ] http/tests/security/xssAuditor/chunked-big-script.html [ Failure ]
+crbug.com/528062 [ Win ] virtual/blink-cors/http/tests/security/XFrameOptions/x-frame-options-cached.html [ Failure ]
+crbug.com/528062 [ Win ] virtual/blink-cors/http/tests/security/contentSecurityPolicy/cached-frame-csp.html [ Failure ]
+crbug.com/528062 [ Win ] virtual/blink-cors/http/tests/security/xssAuditor/cached-frame.html [ Failure ]
+crbug.com/528062 [ Win ] virtual/blink-cors/http/tests/security/xssAuditor/chunked-big-script.html [ Failure ]
 
 # When drawing subpixel smoothed glyphs, CoreGraphics will fake bold the glyphs.
 # In this configuration, the pixel smoothed glyphs will be created from subpixel smoothed glyphs.
@@ -2435,6 +2443,7 @@
 crbug.com/501659 fast/xsl/xslt-missing-namespace-in-xslt.xml [ Failure ]
 
 crbug.com/501659 http/tests/security/xss-DENIED-xml-external-entity.xhtml [ Failure ]
+crbug.com/501659 virtual/blink-cors/http/tests/security/xss-DENIED-xml-external-entity.xhtml [ Failure ]
 crbug.com/501659 fast/css/stylesheet-candidate-nodes-crash.xhtml [ Failure ]
 
 crbug.com/591500 [ Win10 ] printing/webgl-repeated-printing.html [ Failure ]
@@ -2543,6 +2552,7 @@
 
 # Skip the non-virtualized CORS-RFC1918 tests:
 crbug.com/763830 http/tests/security/cors-rfc1918/ [ Skip ]
+crbug.com/763830 virtual/blink-cors/http/tests/security/cors-rfc1918/ [ Skip ]
 
 crbug.com/831729 external/wpt/event-timing/crossiframe.html [ Timeout ]
 crbug.com/831729 external/wpt/event-timing/observer-manual.html [ Skip ]
@@ -2584,6 +2594,7 @@
 
 # Remove from virtual tests when FreezeUserAgent is turned on by default.
 crbug.com/955620 http/tests/navigation/frozen-useragent.html [ Skip ]
+crbug.com/955620 virtual/blink-cors/http/tests/navigation/frozen-useragent.html [ Skip ]
 crbug.com/955620 virtual/stable/http/tests/navigation/frozen-useragent.html [ Skip ]
 
 crbug.com/863896 http/tests/permissions/test-query.html [ Timeout ]
@@ -2657,6 +2668,7 @@
 
 # Failure messages are unstable so we cannot create baselines.
 crbug.com/832071 external/wpt/service-workers/service-worker/worker-client-id.https.html [ Failure ]
+crbug.com/832071 virtual/blink-cors/external/wpt/service-workers/service-worker/worker-client-id.https.html [ Failure ]
 crbug.com/832071 virtual/navigation-mojo-response/external/wpt/service-workers/service-worker/worker-client-id.https.html [ Failure ]
 crbug.com/832071 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/worker-client-id.https.html [ Timeout ]
 
@@ -2810,7 +2822,6 @@
 crbug.com/626703 [ Mac10.12 ] external/wpt/preload/onerror-event.html [ Failure Timeout ]
 crbug.com/626703 [ Mac10.13 ] external/wpt/preload/onerror-event.html [ Failure Timeout ]
 crbug.com/626703 [ Retina ] external/wpt/preload/onerror-event.html [ Timeout ]
-crbug.com/626703 [ Win7 ] external/wpt/service-workers/service-worker/fetch-event-async-respond-with.https.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-digits-001-manual.html [ Skip ]
 crbug.com/626703 external/wpt/web-animations/timing-model/timelines/update-and-send-events-replacement.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-menulist-001.html [ Failure ]
@@ -2850,8 +2861,9 @@
 crbug.com/626703 external/wpt/infrastructure/reftest/reftest_fuzzy_ini_ref_only.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/html/rendering/widgets/button-layout/propagate-text-decoration.html [ Failure ]
 crbug.com/626703 external/wpt/infrastructure/reftest/reftest_fuzzy_ini_short.html [ Failure ]
-crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/abort-after-stop.any.worker.html [ Timeout ]
 crbug.com/626703 external/wpt/xhr/abort-after-stop.any.worker.html [ Timeout ]
+crbug.com/626703 virtual/blink-cors/external/wpt/xhr/abort-after-stop.any.worker.html [ Timeout ]
+crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/abort-after-stop.any.worker.html [ Timeout ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright.html [ Failure ]
@@ -2921,6 +2933,8 @@
 crbug.com/626703 external/wpt/css/css-scroll-snap/scroll-target-margin-003.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-001.html [ Failure ]
 crbug.com/626703 external/wpt/xhr/event-readystatechange-loaded.any.worker.html [ Timeout Failure ]
+crbug.com/626703 virtual/blink-cors/external/wpt/xhr/event-readystatechange-loaded.any.worker.html [ Timeout Failure ]
+crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/event-readystatechange-loaded.any.worker.html [ Timeout Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-003.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/line-breaking/line-breaking-017.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-scroll-snap/scroll-target-padding-003.html [ Failure ]
@@ -2929,7 +2943,6 @@
 crbug.com/626703 external/wpt/css/css-lists/li-list-item-counter.html [ Failure ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-scroll-snap/scroll-target-snap-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-006.html [ Failure ]
-crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/event-readystatechange-loaded.any.html [ Timeout Failure ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-scroll-snap/scroll-target-margin-003.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-scroll-snap/scroll-target-align-003.html [ Failure ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-scroll-snap/scroll-target-align-002.html [ Failure ]
@@ -2943,6 +2956,8 @@
 crbug.com/626703 external/wpt/css/css-text/text-transform/text-transform-multiple-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-lists/li-value-counter-reset-001.html [ Failure ]
 crbug.com/626703 external/wpt/xhr/event-readystatechange-loaded.any.html [ Timeout Failure ]
+crbug.com/626703 virtual/blink-cors/external/wpt/xhr/event-readystatechange-loaded.any.html [ Timeout Failure ]
+crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/event-readystatechange-loaded.any.html [ Timeout Failure ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-scroll-snap/scroll-target-margin-002.html [ Failure ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-scroll-snap/scroll-target-padding-002.html [ Failure ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-scroll-snap/scroll-target-snap-003.html [ Failure ]
@@ -2952,7 +2967,6 @@
 crbug.com/626703 external/wpt/css/css-scroll-snap/scroll-target-padding-001.html [ Failure ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-scroll-snap/scroll-target-padding-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-scroll-snap/scroll-target-snap-003.html [ Failure ]
-crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/event-readystatechange-loaded.any.worker.html [ Timeout Failure ]
 crbug.com/626703 external/wpt/css/css-scroll-snap/scroll-target-snap-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-scroll-snap/scroll-target-margin-002.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-005.html [ Failure ]
@@ -3094,11 +3108,12 @@
 crbug.com/626703 external/wpt/svg/painting/marker-006.svg [ Failure ]
 crbug.com/626703 external/wpt/svg/painting/marker-005.svg [ Failure ]
 crbug.com/906369 external/wpt/css/css-text/text-transform/text-transform-capitalize-033.html [ Failure ]
-crbug.com/626703 virtual/streams-native/external/wpt/fetch/content-type/response.window.html [ Timeout ]
 crbug.com/626703 [ Mac10.10 ] external/wpt/mimesniff/mime-types/parsing.any.worker.html [ Failure Timeout ]
 crbug.com/626703 [ Mac10.11 ] external/wpt/mimesniff/mime-types/parsing.any.worker.html [ Failure Timeout ]
 crbug.com/626703 [ Win ] external/wpt/html/semantics/embedded-content/media-elements/ready-states/autoplay-hidden.optional.html [ Failure Timeout ]
 crbug.com/626703 external/wpt/fetch/content-type/response.window.html [ Timeout ]
+crbug.com/626703 virtual/blink-cors/external/wpt/fetch/content-type/response.window.html [ Timeout ]
+crbug.com/626703 virtual/streams-native/external/wpt/fetch/content-type/response.window.html [ Timeout ]
 crbug.com/626703 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-manual-module.html [ Timeout ]
 crbug.com/626703 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-manual-classic.html [ Timeout ]
 crbug.com/626703 external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-manual-classic.html [ Timeout ]
@@ -3175,6 +3190,11 @@
 crbug.com/626703 external/wpt/referrer-policy/css-integration/svg/internal-stylesheet.html [ Timeout Failure ]
 crbug.com/626703 external/wpt/referrer-policy/css-integration/svg/presentation-attribute.html [ Timeout Failure ]
 crbug.com/626703 external/wpt/referrer-policy/css-integration/svg/processing-instruction.html [ Timeout Failure ]
+crbug.com/626703 virtual/blink-cors/external/wpt/referrer-policy/css-integration/svg/external-stylesheet.html [ Timeout Failure ]
+crbug.com/626703 virtual/blink-cors/external/wpt/referrer-policy/css-integration/svg/inline-style.html [ Timeout Failure ]
+crbug.com/626703 virtual/blink-cors/external/wpt/referrer-policy/css-integration/svg/internal-stylesheet.html [ Timeout Failure ]
+crbug.com/626703 virtual/blink-cors/external/wpt/referrer-policy/css-integration/svg/presentation-attribute.html [ Timeout Failure ]
+crbug.com/626703 virtual/blink-cors/external/wpt/referrer-policy/css-integration/svg/processing-instruction.html [ Timeout Failure ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/referrer-policy/css-integration/svg/external-stylesheet.html [ Timeout Failure ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/referrer-policy/css-integration/svg/inline-style.html [ Timeout Failure ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/referrer-policy/css-integration/svg/internal-stylesheet.html [ Timeout Failure ]
@@ -3217,6 +3237,42 @@
 crbug.com/906959 external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
 crbug.com/906959 external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
 crbug.com/906959 external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/no-referrer/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/no-referrer/http-rp/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin/http-rp/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/same-origin-insecure.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/same-insecure.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-http/shared-worker/no-redirect/same-insecure.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/same-insecure.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-http/shared-worker/no-redirect/same-insecure.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
+crbug.com/906959 virtual/blink-cors/external/wpt/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
 crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
 crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/shared-worker/no-redirect/insecure-protocol.http.html [ Failure ]
 crbug.com/906959 virtual/omt-worker-fetch/external/wpt/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/insecure-protocol.http.html [ Failure ]
@@ -3378,6 +3434,7 @@
 crbug.com/626703 external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/navigation.sub.html?encoding=utf8 [ Timeout ]
 crbug.com/626703 external/wpt/css/css-transforms/transform-box/view-box-mutation.html [ Failure ]
 crbug.com/626703 external/wpt/fetch/security/redirect-to-url-with-credentials.https.html [ Timeout ]
+crbug.com/626703 virtual/blink-cors/external/wpt/fetch/security/redirect-to-url-with-credentials.https.html [ Timeout ]
 crbug.com/626703 virtual/streams-native/external/wpt/fetch/security/redirect-to-url-with-credentials.https.html [ Timeout ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-024.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-048.html [ Failure ]
@@ -3465,6 +3522,7 @@
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-010.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-009.html [ Failure ]
 crbug.com/626703 external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
+crbug.com/626703 virtual/blink-cors/external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
 crbug.com/626703 virtual/streams-native/external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-fonts/font-feature-settings-descriptor-01.html [ Failure ]
 crbug.com/626703 [ Win10 ] external/wpt/fetch/api/redirect/redirect-count.any.worker.html [ Timeout ]
@@ -3718,7 +3776,8 @@
 crbug.com/626703 external/wpt/requestidlecallback/callback-xhr-sync.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/omt-worker-fetch/external/wpt/service-workers/service-worker/update-bytecheck.https.html [ Timeout ]
+crbug.com/648295 virtual/blink-cors/external/wpt/service-workers/service-worker/update-bytecheck.https.html [ Timeout ]
+crbug.com/648295 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/update-bytecheck.https.html [ Timeout ]
 crbug.com/626703 external/wpt/svg/linking/reftests/href-filter-element.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/2_cues_overlapping_completely_move_up.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/2_cues_overlapping_partially_move_down.html [ Failure ]
@@ -3932,8 +3991,10 @@
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/too_many_cues_wrapped.html [ Failure ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/event-readystatechange-loaded.htm [ Failure Timeout ]
 crbug.com/626703 external/wpt/xhr/preserve-ua-header-on-redirect.htm [ Failure ]
+crbug.com/626703 virtual/blink-cors/external/wpt/xhr/preserve-ua-header-on-redirect.htm [ Failure ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/preserve-ua-header-on-redirect.htm [ Failure ]
 crbug.com/626703 external/wpt/xhr/setrequestheader-header-allowed.htm [ Failure ]
+crbug.com/626703 virtual/blink-cors/external/wpt/xhr/setrequestheader-header-allowed.htm [ Failure ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/xhr/setrequestheader-header-allowed.htm [ Failure ]
 crbug.com/626703 [ Win10 ] external/wpt/preload/delaying-onload-link-preload-after-discovery.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/css/css-writing-modes/box-offsets-rel-pos-vlr-005.xht [ Failure ]
@@ -4009,6 +4070,8 @@
 # Can't add baselines because a generated unique ID is part of the failure message.
 crbug.com/888470 external/wpt/referrer-policy/css-integration/child-css/internal-import-stylesheet.html [ Failure ]
 crbug.com/888470 external/wpt/referrer-policy/css-integration/child-css/processing-instruction.html [ Failure ]
+crbug.com/888470 virtual/blink-cors/external/wpt/referrer-policy/css-integration/child-css/internal-import-stylesheet.html [ Failure ]
+crbug.com/888470 virtual/blink-cors/external/wpt/referrer-policy/css-integration/child-css/processing-instruction.html [ Failure ]
 crbug.com/888470 virtual/omt-worker-fetch/external/wpt/referrer-policy/css-integration/child-css/internal-import-stylesheet.html [ Failure ]
 crbug.com/888470 virtual/omt-worker-fetch/external/wpt/referrer-policy/css-integration/child-css/processing-instruction.html [ Failure ]
 
@@ -4042,6 +4105,7 @@
 
 # Unclear if XHR events should still be fired after its frame is discarded.
 crbug.com/881180 external/wpt/xhr/open-url-multi-window-4.htm [ Timeout ]
+crbug.com/881180 virtual/blink-cors/external/wpt/xhr/open-url-multi-window-4.htm [ Timeout ]
 crbug.com/881180 virtual/omt-worker-fetch/external/wpt/xhr/open-url-multi-window-4.htm [ Timeout ]
 
 crbug.com/655458 external/wpt/workers/Worker_terminate_event_queue.htm [ Pass Timeout ]
@@ -4137,11 +4201,12 @@
 
 # This test requires a special browser flag and seems not suitable for a wpt test, see bug.
 crbug.com/691944 external/wpt/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
+crbug.com/691944 virtual/blink-cors/external/wpt/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
 crbug.com/691944 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
 
 # These tests (erroneously) see a platform-specific User-Agent header
 crbug.com/595993 external/wpt/service-workers/service-worker/fetch-header-visibility.https.html [ Failure ]
-crbug.com/595993 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
+crbug.com/595993 virtual/blink-cors/external/wpt/service-workers/service-worker/fetch-header-visibility.https.html [ Failure ]
 crbug.com/595993 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/fetch-header-visibility.https.html [ Failure ]
 
 crbug.com/619427 [ Mac ] fast/overflow/overflow-height-float-not-removed-crash3.html [ Pass Failure ]
@@ -4168,6 +4233,7 @@
 
 # Added 2016-12-12
 crbug.com/610835 http/tests/security/XFrameOptions/x-frame-options-deny-multiple-clients.html [ Failure Pass ]
+crbug.com/610835 virtual/blink-cors/http/tests/security/XFrameOptions/x-frame-options-deny-multiple-clients.html [ Failure Pass ]
 
 #crbug.com/765738 [ Linux Win Mac ] http/tests/wasm/wasm_remote_postMessage_test.https.html [ Pass Timeout ]
 crbug.com/892212 http/tests/wasm/wasm_remote_postMessage_test.https.html [ Pass Failure Timeout ]
@@ -4326,6 +4392,8 @@
 # Sheriff failures 2017-05-11
 crbug.com/724027 http/tests/security/contentSecurityPolicy/directive-parsing-03.html [ Skip ]
 crbug.com/724027 http/tests/security/contentSecurityPolicy/source-list-parsing-04.html [ Skip ]
+crbug.com/724027 virtual/blink-cors/http/tests/security/contentSecurityPolicy/directive-parsing-03.html [ Skip ]
+crbug.com/724027 virtual/blink-cors/http/tests/security/contentSecurityPolicy/source-list-parsing-04.html [ Skip ]
 
 # Sheriff failures 2017-05-16
 crbug.com/722212 fast/events/pointerevents/mouse-pointer-event-properties.html [ Failure Timeout Pass ]
@@ -4407,6 +4475,7 @@
 
 # Sheriff failures 2017-07-03
 crbug.com/708994 http/tests/security/cross-frame-mouse-source-capabilities.html [ Timeout Pass ]
+crbug.com/708994 virtual/blink-cors/http/tests/security/cross-frame-mouse-source-capabilities.html [ Timeout Pass ]
 
 crbug.com/745887 [ Mac ] fast/frames/sandboxed-iframe-plugins.html [ Failure Pass ]
 crbug.com/745887 [ Win ] fast/frames/sandboxed-iframe-plugins.html [ Failure Pass ]
@@ -4568,6 +4637,7 @@
 
 # Sheriff failures 2017-09-21
 crbug.com/767469 http/tests/navigation/start-load-during-provisional-loader-detach.html [ Pass Failure ]
+crbug.com/767469 virtual/blink-cors/http/tests/navigation/start-load-during-provisional-loader-detach.html [ Pass Failure ]
 crbug.com/767469 virtual/stable/http/tests/navigation/start-load-during-provisional-loader-detach.html [ Pass Failure ]
 
 # Sheriff failures 2017-10-02
@@ -5166,6 +5236,7 @@
 crbug.com/881207 fast/js/regress/splice-to-remove.html [ Timeout Pass ]
 
 crbug.com/882689 http/tests/security/cookies/third-party-cookie-blocking-worker.html [ Pass Failure ]
+crbug.com/882689 virtual/blink-cors/http/tests/security/cookies/third-party-cookie-blocking-worker.html [ Pass Failure ]
 
 # The following tests need LazyFrameLoading.
 crbug.com/869492 external/wpt/feature-policy/experimental-features/lazyload/lazyload-enabled-tentative.sub.html [ Failure ]
@@ -5479,6 +5550,8 @@
 
 # These started failing when network service was enabled by default.
 crbug.com/933880 external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
+crbug.com/933880 virtual/blink-cors/external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
+crbug.com/933880 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
 crbug.com/933880 http/tests/inspector-protocol/network/interception-take-stream.js [ Failure ]
 crbug.com/933880 http/tests/inspector-protocol/network/xhr-interception-auth-fail.js [ Failure ]
 # This passes in content_shell but not in chrome with network service disabled,
@@ -5765,3 +5838,6 @@
 crbug.com/966345 external/wpt/webvtt/rendering/cues-with-video/processing-model/3_tracks.html [ Failure ]
 crbug.com/966345 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_white-space_normal_wrapped.html [ Failure ]
 crbug.com/966345 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_white-space_pre-line_wrapped.html [ Failure ]
+
+crbug.com/905971 virtual/blink-cors/http/tests/security/img-redirect-to-crossorigin-credentials.html [ Failure ]
+crbug.com/905971 virtual/blink-cors/http/tests/security/script-crossorigin-redirect-credentials.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 3498184..a1e6ca1 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -988,5 +988,50 @@
     "prefix": "cookies-without-samesite-must-be-secure",
     "base": "external/wpt/cookies/samesite-none-secure",
     "args": ["--enable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure"]
+  },
+  {
+    "prefix": "blink-cors",
+    "base": "external/wpt/fetch",
+    "args": ["--disable-features=OutOfBlinkCors"]
+  },
+  {
+    "prefix": "blink-cors",
+    "base": "external/wpt/http",
+    "args": ["--disable-features=OutOfBlinkCors"]
+  },
+  {
+    "prefix": "blink-cors",
+    "base": "external/wpt/referrer-policy",
+    "args": ["--disable-features=OutOfBlinkCors"]
+  },
+  {
+    "prefix": "blink-cors",
+    "base": "external/wpt/service-workers",
+    "args": ["--disable-features=OutOfBlinkCors"]
+  },
+  {
+    "prefix": "blink-cors",
+    "base": "external/wpt/xhr",
+    "args": ["--disable-features=OutOfBlinkCors"]
+  },
+  {
+    "prefix": "blink-cors",
+    "base": "http/tests/fetch",
+    "args": ["--disable-features=OutOfBlinkCors"]
+  },
+  {
+    "prefix": "blink-cors",
+    "base": "http/tests/navigation",
+    "args": ["--disable-features=OutOfBlinkCors"]
+  },
+  {
+    "prefix": "blink-cors",
+    "base": "http/tests/security",
+    "args": ["--disable-features=OutOfBlinkCors"]
+  },
+  {
+    "prefix": "blink-cors",
+    "base": "http/tests/xmlhttprequest",
+    "args": ["--disable-features=OutOfBlinkCors"]
   }
 ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/inheritance-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/inheritance-expected.txt
index b0c6f2fb..9fdd188 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/inheritance-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/inheritance-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL Property color-scheme has initial value auto assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
+FAIL Property color-scheme has initial value normal assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
 FAIL Property color-scheme inherits assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/inheritance.html b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/inheritance.html
index c48d2ad..f7f65293 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/inheritance.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/inheritance.html
@@ -9,5 +9,5 @@
   <div id="target"></div>
 </div>
 <script>
-  assert_inherited('color-scheme', 'auto', 'light dark');
+  assert_inherited('color-scheme', 'normal', 'light dark');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-computed.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-computed.tentative-expected.txt
index 35ae840..0aa58bb9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-computed.tentative-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-computed.tentative-expected.txt
@@ -1,17 +1,17 @@
 This is a testharness.js-based test.
-FAIL Property color-scheme value '' computes to 'auto' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
-FAIL Property color-scheme value 'auto' computes to 'auto' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
+FAIL Property color-scheme value '' computes to 'normal' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
+FAIL Property color-scheme value 'normal' computes to 'normal' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
 FAIL Property color-scheme value 'light dark' computes to 'light dark' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
 FAIL Property color-scheme value 'dark light' computes to 'dark light' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
 FAIL Property color-scheme value 'light unknown' computes to 'light unknown' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
 FAIL Property color-scheme value 'only light' computes to 'only light' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
 FAIL Property color-scheme value 'light light' computes to 'light light' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
 FAIL Property color-scheme value 'light only' computes to 'light only' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
-FAIL Property color-scheme value 'initial' computes to 'auto' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
-FAIL Property color-scheme value 'inherit' computes to 'auto' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
-FAIL Property color-scheme value 'unset' computes to 'auto' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
-FAIL Property color-scheme value 'revert' computes to 'auto' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
-FAIL Property color-scheme value 'default' computes to 'auto' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
-FAIL Property color-scheme value 'none' computes to 'auto' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
+FAIL Property color-scheme value 'initial' computes to 'normal' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
+FAIL Property color-scheme value 'inherit' computes to 'normal' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
+FAIL Property color-scheme value 'unset' computes to 'normal' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
+FAIL Property color-scheme value 'revert' computes to 'normal' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
+FAIL Property color-scheme value 'default' computes to 'normal' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
+FAIL Property color-scheme value 'none' computes to 'normal' assert_true: color-scheme doesn't seem to be supported in the computed style expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-computed.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-computed.tentative.html
index 74d1bb9..80b98039 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-computed.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-computed.tentative.html
@@ -6,8 +6,8 @@
 <script src="/css/support/computed-testcommon.js"></script>
 <div id="target"></div>
 <script>
-  test_computed_value("color-scheme", "", "auto");
-  test_computed_value("color-scheme", "auto");
+  test_computed_value("color-scheme", "", "normal");
+  test_computed_value("color-scheme", "normal");
   test_computed_value("color-scheme", "light dark");
   test_computed_value("color-scheme", "dark light");
   test_computed_value("color-scheme", "light unknown");
@@ -15,10 +15,10 @@
   test_computed_value("color-scheme", "light light");
   test_computed_value("color-scheme", "light only");
   // reserved and css-wide keywords.
-  test_computed_value("color-scheme", "initial", "auto");
-  test_computed_value("color-scheme", "inherit", "auto");
-  test_computed_value("color-scheme", "unset", "auto");
-  test_computed_value("color-scheme", "revert", "auto");
-  test_computed_value("color-scheme", "default", "auto");
-  test_computed_value("color-scheme", "none", "auto");
+  test_computed_value("color-scheme", "initial", "normal");
+  test_computed_value("color-scheme", "inherit", "normal");
+  test_computed_value("color-scheme", "unset", "normal");
+  test_computed_value("color-scheme", "revert", "normal");
+  test_computed_value("color-scheme", "default", "normal");
+  test_computed_value("color-scheme", "none", "normal");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-invalid.html
index 0def5b3..48fa4d1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-invalid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-invalid.html
@@ -8,13 +8,13 @@
   test_invalid_value("color-scheme", "none");
   test_invalid_value("color-scheme", "only");
   test_invalid_value("color-scheme", "only only");
-  test_invalid_value("color-scheme", "only auto");
+  test_invalid_value("color-scheme", "only normal");
   test_invalid_value("color-scheme", "only light dark");
   test_invalid_value("color-scheme", "only light light");
   test_invalid_value("color-scheme", "only light purple");
-  test_invalid_value("color-scheme", "auto dark");
-  test_invalid_value("color-scheme", "auto purple");
-  test_invalid_value("color-scheme", "light auto");
+  test_invalid_value("color-scheme", "normal dark");
+  test_invalid_value("color-scheme", "normal purple");
+  test_invalid_value("color-scheme", "light normal");
   test_invalid_value("color-scheme", "light none");
   test_invalid_value("color-scheme", "light, dark");
   test_invalid_value("color-scheme", "light inherit");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-valid-expected.txt
index 4839a16..4fd64bb6 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-valid-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-valid-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL e.style['color-scheme'] = "auto" should set the property value assert_not_equals: property should be set got disallowed value ""
+FAIL e.style['color-scheme'] = "normal" should set the property value assert_not_equals: property should be set got disallowed value ""
 FAIL e.style['color-scheme'] = "light" should set the property value assert_not_equals: property should be set got disallowed value ""
 FAIL e.style['color-scheme'] = "dark" should set the property value assert_not_equals: property should be set got disallowed value ""
 FAIL e.style['color-scheme'] = "light dark" should set the property value assert_not_equals: property should be set got disallowed value ""
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-valid.html b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-valid.html
index 43361006..2ba01da3 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-valid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-color-adjust/parsing/color-scheme-valid.html
@@ -5,7 +5,7 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/parsing-testcommon.js"></script>
 <script>
-  test_valid_value("color-scheme", "auto");
+  test_valid_value("color-scheme", "normal");
   test_valid_value("color-scheme", "light");
   test_valid_value("color-scheme", "dark");
   test_valid_value("color-scheme", "light dark");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/trailing-space-align-start-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/trailing-space-align-start-ref.html
new file mode 100644
index 0000000..b65a8fe
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/trailing-space-align-start-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+div {
+  font-family: monospace;
+  font-size: 30px;
+  width: 3ch;
+  overflow: auto;
+  white-space: pre;
+}
+</style>
+<body>
+  <div>0 00</div>
+  <div>0 0</div>
+  <div>0 0</div>
+  <div>0 0</div>
+  <div>0 0<br>0 0</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/trailing-space-align-start.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/trailing-space-align-start.tentative.html
new file mode 100644
index 0000000..7908de1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/trailing-space-align-start.tentative.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>Preserved trailing spaces when `text-align: left`</title>
+<link rel="help" href="http://crbug.com/966773">
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<link rel="match" href="reference/trailing-space-align-start-ref.html">
+<style>
+div {
+  font-family: monospace;
+  font-size: 30px;
+  width: 3ch;
+  overflow: auto;
+}
+.pre { white-space: pre; }
+.pre-wrap { white-space: pre-wrap; }
+</style>
+<body>
+  <div class="pre">0 0 </div>
+  <div class="pre">0 0</div>
+  <div class="pre-wrap">0 0</div>
+  <div class="pre-wrap">0 0 </div>
+  <div class="pre-wrap">0 0 0 0 </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-async-respond-with.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-async-respond-with.https.html
index 7842a82..ae64fcb9 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-async-respond-with.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-async-respond-with.https.html
@@ -21,23 +21,33 @@
   frame = await with_iframe(scope);
 }, 'global setup');
 
+// Waits for a single message from the service worker and then removes the
+// message handler. Not safe for concurrent use.
+function wait_for_message() {
+  return new Promise((resolve) => {
+    const handler = (event) => {
+      navigator.serviceWorker.removeEventListener('message', handler);
+      resolve(event.data);
+    };
+    navigator.serviceWorker.addEventListener('message', handler);
+  });
+}
+
 // Does one test case. It fetches |url|. The service worker gets a fetch event
 // for |url| and attempts to call respondWith() asynchronously. It reports back
 // to the test whether an exception was thrown.
 async function do_test(url) {
   // Send a message to tell the worker a new test case is starting.
-  const sawMessage = new Promise(resolve => {
-    navigator.serviceWorker.onmessage = (event) => {
-      resolve(event.data);
-    };
-    worker.postMessage('');
-  });
+  const message = wait_for_message();
+  worker.postMessage('initializeMessageHandler');
+  const response = await message;
+  assert_equals(response, 'messageHandlerInitialized');
 
   // Start a fetch.
   frame.contentWindow.fetch(url);
 
   // Receive the test result from the service worker.
-  return await sawMessage;
+  return wait_for_message();
 };
 
 promise_test(async (t) => {
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js
index 3409d0a..dc3f1a1 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js
@@ -4,18 +4,28 @@
 
 // These get reset at the start of a test case.
 let reportResult;
-let resultPromise;
 
 // The test page sends a message to tell us that a new test case is starting.
 // We expect a fetch event after this.
 self.addEventListener('message', (event) => {
-  resultPromise = new Promise((resolve) => {
+  // Ensure tests run mutually exclusive.
+  if (reportResult) {
+    event.source.postMessage('testAlreadyRunning');
+    return;
+  }
+
+  const resultPromise = new Promise((resolve) => {
     reportResult = resolve;
+    // Tell the client that everything is initialized and that it's safe to
+    // proceed with the test without relying on the order of events (which some
+    // browsers like Chrome may not guarantee).
+    event.source.postMessage('messageHandlerInitialized');
   });
 
   // Keep the worker alive until the test case finishes, and report
   // back the result to the test page.
   event.waitUntil(resultPromise.then(result => {
+    reportResult = null;
     event.source.postMessage(result);
   }));
 });
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https-expected.txt
index 1a941ec8..c078662 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https-expected.txt
@@ -3,13 +3,13 @@
 PASS [audio] RTCRtpSynchronizationSource.timestamp is a number
 PASS [audio] RTCRtpSynchronizationSource.rtpTimestamp is a number [0, 2^32-1]
 PASS [audio] getSynchronizationSources() does not contain SSRCs older than 10 seconds
-FAIL [audio] RTCRtpSynchronizationSource.timestamp is comparable to performance.timeOrigin + performance.now() assert_true: expected true got false
+PASS [audio] RTCRtpSynchronizationSource.timestamp is comparable to performance.timeOrigin + performance.now()
 PASS [audio] RTCRtpSynchronizationSource.source is a number
 PASS [video] getSynchronizationSources() eventually returns a non-empty list
 PASS [video] RTCRtpSynchronizationSource.timestamp is a number
 PASS [video] RTCRtpSynchronizationSource.rtpTimestamp is a number [0, 2^32-1]
 PASS [video] getSynchronizationSources() does not contain SSRCs older than 10 seconds
-FAIL [video] RTCRtpSynchronizationSource.timestamp is comparable to performance.timeOrigin + performance.now() assert_true: expected true got false
+PASS [video] RTCRtpSynchronizationSource.timestamp is comparable to performance.timeOrigin + performance.now()
 PASS [video] RTCRtpSynchronizationSource.source is a number
 PASS [audio-only] RTCRtpSynchronizationSource.audioLevel is a number [0, 1]
 FAIL [audio-only] RTCRtpSynchronizationSource.voiceActivityFlag is a boolean assert_equals: expected "boolean" but got "undefined"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/text/selection/pre-wrap-overflow-selection-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/text/selection/pre-wrap-overflow-selection-expected.png
index f6e1d2ad..3983243 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/text/selection/pre-wrap-overflow-selection-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/text/selection/pre-wrap-overflow-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/blink-cors/external/wpt/fetch/README.txt b/third_party/blink/web_tests/virtual/blink-cors/external/wpt/fetch/README.txt
new file mode 100644
index 0000000..19936c1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/external/wpt/fetch/README.txt
@@ -0,0 +1 @@
+This directory is for testing Blink CORS implementation.
diff --git a/third_party/blink/web_tests/virtual/blink-cors/external/wpt/http/README.txt b/third_party/blink/web_tests/virtual/blink-cors/external/wpt/http/README.txt
new file mode 100644
index 0000000..19936c1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/external/wpt/http/README.txt
@@ -0,0 +1 @@
+This directory is for testing Blink CORS implementation.
diff --git a/third_party/blink/web_tests/virtual/blink-cors/external/wpt/referrer-policy/README.txt b/third_party/blink/web_tests/virtual/blink-cors/external/wpt/referrer-policy/README.txt
new file mode 100644
index 0000000..19936c1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/external/wpt/referrer-policy/README.txt
@@ -0,0 +1 @@
+This directory is for testing Blink CORS implementation.
diff --git a/third_party/blink/web_tests/virtual/blink-cors/external/wpt/service-workers/README.txt b/third_party/blink/web_tests/virtual/blink-cors/external/wpt/service-workers/README.txt
new file mode 100644
index 0000000..19936c1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/external/wpt/service-workers/README.txt
@@ -0,0 +1 @@
+This directory is for testing Blink CORS implementation.
diff --git a/third_party/blink/web_tests/virtual/blink-cors/external/wpt/service-workers/service-worker/fetch-request-xhr.https-expected.txt b/third_party/blink/web_tests/virtual/blink-cors/external/wpt/service-workers/service-worker/fetch-request-xhr.https-expected.txt
new file mode 100644
index 0000000..44c6a05
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/external/wpt/service-workers/service-worker/fetch-request-xhr.https-expected.txt
@@ -0,0 +1,16 @@
+This is a testharness.js-based test.
+PASS initialize global state
+FAIL event.request has the expected headers for same-origin GET. promise_test: Unhandled rejection with value: object "Error: assert_array_equals: event.request has the expected headers for same-origin GET. lengths differ, expected 1 got 3"
+FAIL event.request has the expected headers for same-origin POST. promise_test: Unhandled rejection with value: object "Error: assert_array_equals: event.request has the expected headers for same-origin POST. lengths differ, expected 2 got 5"
+FAIL event.request has the expected headers for cross-origin GET. promise_test: Unhandled rejection with value: object "Error: assert_array_equals: event.request has the expected headers for cross-origin GET. lengths differ, expected 1 got 3"
+FAIL event.request has the expected headers for cross-origin POST. promise_test: Unhandled rejection with value: object "Error: assert_array_equals: event.request has the expected headers for cross-origin POST. lengths differ, expected 2 got 5"
+PASS FetchEvent#request.body contains XHR request data (string)
+PASS FetchEvent#request.body contains XHR request data (blob)
+PASS FetchEvent#request.method is set to XHR method
+PASS XHR using OPTIONS method
+PASS XHR with form data
+PASS XHR with mode/credentials set
+PASS XHR to data URL
+PASS restore global state
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/blink-cors/external/wpt/xhr/README.txt b/third_party/blink/web_tests/virtual/blink-cors/external/wpt/xhr/README.txt
new file mode 100644
index 0000000..19936c1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/external/wpt/xhr/README.txt
@@ -0,0 +1 @@
+This directory is for testing Blink CORS implementation.
diff --git a/third_party/blink/web_tests/virtual/blink-cors/http/tests/fetch/README.txt b/third_party/blink/web_tests/virtual/blink-cors/http/tests/fetch/README.txt
new file mode 100644
index 0000000..19936c1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/http/tests/fetch/README.txt
@@ -0,0 +1 @@
+This directory is for testing Blink CORS implementation.
diff --git a/third_party/blink/web_tests/virtual/blink-cors/http/tests/navigation/README.txt b/third_party/blink/web_tests/virtual/blink-cors/http/tests/navigation/README.txt
new file mode 100644
index 0000000..19936c1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/http/tests/navigation/README.txt
@@ -0,0 +1 @@
+This directory is for testing Blink CORS implementation.
diff --git a/third_party/blink/web_tests/virtual/blink-cors/http/tests/security/README.txt b/third_party/blink/web_tests/virtual/blink-cors/http/tests/security/README.txt
new file mode 100644
index 0000000..19936c1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/http/tests/security/README.txt
@@ -0,0 +1 @@
+This directory is for testing Blink CORS implementation.
diff --git a/third_party/blink/web_tests/virtual/blink-cors/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture-expected.txt b/third_party/blink/web_tests/virtual/blink-cors/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture-expected.txt
new file mode 100644
index 0000000..96c842b
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture-expected.txt
@@ -0,0 +1,8 @@
+CONSOLE ERROR: line 8: Unsafe JavaScript attempt to initiate navigation for frame with origin 'http://127.0.0.1:8000' from frame with URL 'http://localhost:8000/security/frameNavigation/resources/iframe-that-performs-top-navigation-without-user-gesture.html'. The frame attempting navigation is targeting its top-level window, but is neither same-origin with its target nor has it received a user gesture. See https://www.chromestatus.com/features/5851021045661696.
+
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+The navigation should fail. This text should be visible.
diff --git a/third_party/blink/web_tests/virtual/blink-cors/http/tests/xmlhttprequest/README.txt b/third_party/blink/web_tests/virtual/blink-cors/http/tests/xmlhttprequest/README.txt
new file mode 100644
index 0000000..19936c1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/http/tests/xmlhttprequest/README.txt
@@ -0,0 +1 @@
+This directory is for testing Blink CORS implementation.
diff --git a/third_party/blink/web_tests/virtual/blink-cors/http/tests/xmlhttprequest/cross-origin-unsupported-url-expected.txt b/third_party/blink/web_tests/virtual/blink-cors/http/tests/xmlhttprequest/cross-origin-unsupported-url-expected.txt
new file mode 100644
index 0000000..a18432e1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/http/tests/xmlhttprequest/cross-origin-unsupported-url-expected.txt
@@ -0,0 +1,28 @@
+CONSOLE WARNING: line 8: Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
+CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'tel:1234' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'tel:1234' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 29: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 29: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 29: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 29: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 29: Access to XMLHttpRequest at 'tel:1234' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 29: Access to XMLHttpRequest at 'tel:1234' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+This is a testharness.js-based test.
+PASS sync test for url=mailto:foo@bar.com, contentType=undefined
+PASS sync test for url=mailto:foo@bar.com, contentType=application/json
+PASS async test for url=mailto:foo@bar.com, contentType=undefined
+PASS async test for url=mailto:foo@bar.com, contentType=application/json
+PASS sync test for url=localhost:8080/, contentType=undefined
+PASS sync test for url=localhost:8080/, contentType=application/json
+PASS async test for url=localhost:8080/, contentType=undefined
+PASS async test for url=localhost:8080/, contentType=application/json
+PASS sync test for url=tel:1234, contentType=undefined
+PASS sync test for url=tel:1234, contentType=application/json
+PASS async test for url=tel:1234, contentType=undefined
+PASS async test for url=tel:1234, contentType=application/json
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/blink-cors/http/tests/xmlhttprequest/workers/cross-origin-unsupported-url-expected.txt b/third_party/blink/web_tests/virtual/blink-cors/http/tests/xmlhttprequest/workers/cross-origin-unsupported-url-expected.txt
new file mode 100644
index 0000000..086a854
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/blink-cors/http/tests/xmlhttprequest/workers/cross-origin-unsupported-url-expected.txt
@@ -0,0 +1,27 @@
+CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'tel:1234' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'tel:1234' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 29: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 29: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 29: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 29: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 29: Access to XMLHttpRequest at 'tel:1234' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+CONSOLE ERROR: line 29: Access to XMLHttpRequest at 'tel:1234' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
+This is a testharness.js-based test.
+PASS sync test for url=mailto:foo@bar.com, contentType=undefined
+PASS sync test for url=mailto:foo@bar.com, contentType=application/json
+PASS async test for url=mailto:foo@bar.com, contentType=undefined
+PASS async test for url=mailto:foo@bar.com, contentType=application/json
+PASS sync test for url=localhost:8080/, contentType=undefined
+PASS sync test for url=localhost:8080/, contentType=application/json
+PASS async test for url=localhost:8080/, contentType=undefined
+PASS async test for url=localhost:8080/, contentType=application/json
+PASS sync test for url=tel:1234, contentType=undefined
+PASS sync test for url=tel:1234, contentType=application/json
+PASS async test for url=tel:1234, contentType=undefined
+PASS async test for url=tel:1234, contentType=application/json
+Harness: the test ran to completion.
+
diff --git a/tools/grit/BUILD.gn b/tools/grit/BUILD.gn
index b50bdd5..f0368850 100644
--- a/tools/grit/BUILD.gn
+++ b/tools/grit/BUILD.gn
@@ -34,5 +34,6 @@
     "//testing/scripts/run_isolated_script_test.py",
     "//testing/xvfb.py",
     "//tools/grit/",
+    "//third_party/catapult/third_party/typ/",
   ]
 }
diff --git a/tools/grit/grit/shortcuts_unittests.py b/tools/grit/grit/shortcuts_unittest.py
old mode 100755
new mode 100644
similarity index 97%
rename from tools/grit/grit/shortcuts_unittests.py
rename to tools/grit/grit/shortcuts_unittest.py
index b7c37b5..ed788e63
--- a/tools/grit/grit/shortcuts_unittests.py
+++ b/tools/grit/grit/shortcuts_unittest.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -75,5 +74,3 @@
     warnings = shortcuts.GenerateDuplicateShortcutsWarnings(self.uq, 'PROJECT')
     self.failUnless(len(warnings) == 0)
 
-if __name__ == '__main__':
-  unittest.main()
diff --git a/tools/grit/grit/test_suite_all.py b/tools/grit/grit/test_suite_all.py
index 246d4f0f..8d7f76b 100755
--- a/tools/grit/grit/test_suite_all.py
+++ b/tools/grit/grit/test_suite_all.py
@@ -5,135 +5,27 @@
 
 '''Unit test suite that collects all test cases for GRIT.'''
 
-import argparse
-import json
 import os
 import sys
-import unittest
 
 
-# TODO(https://crbug.com/965793) Use unittest.defaultTestLoader to automatically
-# load tests from modules. Iterating over the directory and importing could then
-# automate this all the way, if desired.
+CUR_DIR = os.path.dirname(os.path.realpath(__file__))
+SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname(CUR_DIR)))
+TYP_DIR = os.path.join(
+    SRC_DIR, 'third_party', 'catapult', 'third_party', 'typ')
 
+if TYP_DIR not in sys.path:
+    sys.path.insert(0, TYP_DIR)
 
-class TestSuiteAll(unittest.TestSuite):
-  def __init__(self):
-    super(TestSuiteAll, self).__init__()
-    # Imports placed here to prevent circular imports.
-    # pylint: disable-msg=C6204
-    import grit.clique_unittest
-    import grit.grd_reader_unittest
-    import grit.grit_runner_unittest
-    import grit.lazy_re_unittest
-    import grit.shortcuts_unittests
-    import grit.tclib_unittest
-    import grit.util_unittest
-    import grit.xtb_reader_unittest
-    import grit.format.android_xml_unittest
-    import grit.format.c_format_unittest
-    import grit.format.chrome_messages_json_unittest
-    import grit.format.data_pack_unittest
-    import grit.format.gzip_string_unittest
-    import grit.format.html_inline_unittest
-    import grit.format.policy_templates_json_unittest
-    import grit.format.rc_header_unittest
-    import grit.format.rc_unittest
-    import grit.format.resource_map_unittest
-    import grit.gather.admin_template_unittest
-    import grit.gather.chrome_html_unittest
-    import grit.gather.chrome_scaled_image_unittest
-    import grit.gather.policy_json_unittest
-    import grit.gather.rc_unittest
-    import grit.gather.tr_html_unittest
-    import grit.gather.txt_unittest
-    import grit.node.base_unittest
-    import grit.node.custom.filename_unittest
-    import grit.node.include_unittest
-    import grit.node.message_unittest
-    import grit.node.misc_unittest
-    import grit.node.node_io_unittest
-    import grit.node.structure_unittest
-    import grit.tool.android2grd_unittest
-    import grit.tool.build_unittest
-    import grit.tool.buildinfo_unittest
-    import grit.tool.postprocess_unittest
-    import grit.tool.preprocess_unittest
-    import grit.tool.rc2grd_unittest
-    import grit.tool.transl2tc_unittest
-    import grit.tool.xmb_unittest
+import typ
 
-    test_classes = [
-        grit.clique_unittest.MessageCliqueUnittest,
-        grit.grd_reader_unittest.GrdReaderUnittest,
-        grit.grit_runner_unittest.OptionArgsUnittest,
-        grit.lazy_re_unittest.LazyReUnittest,
-        grit.shortcuts_unittests.ShortcutsUnittest,
-        grit.tclib_unittest.TclibUnittest,
-        grit.util_unittest.UtilUnittest,
-        grit.xtb_reader_unittest.XtbReaderUnittest,
-        grit.format.android_xml_unittest.AndroidXmlUnittest,
-        grit.format.c_format_unittest.CFormatUnittest,
-        grit.format.chrome_messages_json_unittest.
-            ChromeMessagesJsonFormatUnittest,
-        grit.format.data_pack_unittest.FormatDataPackUnittest,
-        grit.format.gzip_string_unittest.FormatGzipStringUnittest,
-        grit.format.html_inline_unittest.HtmlInlineUnittest,
-        grit.format.policy_templates_json_unittest.PolicyTemplatesJsonUnittest,
-        grit.format.rc_header_unittest.RcHeaderFormatterUnittest,
-        grit.format.rc_unittest.FormatRcUnittest,
-        grit.format.resource_map_unittest.FormatResourceMapUnittest,
-        grit.gather.admin_template_unittest.AdmGathererUnittest,
-        grit.gather.chrome_html_unittest.ChromeHtmlUnittest,
-        grit.gather.chrome_scaled_image_unittest.ChromeScaledImageUnittest,
-        grit.gather.policy_json_unittest.PolicyJsonUnittest,
-        grit.gather.rc_unittest.RcUnittest,
-        grit.gather.tr_html_unittest.ParserUnittest,
-        grit.gather.tr_html_unittest.TrHtmlUnittest,
-        grit.gather.txt_unittest.TxtUnittest,
-        grit.node.base_unittest.NodeUnittest,
-        grit.node.custom.filename_unittest.WindowsFilenameUnittest,
-        grit.node.include_unittest.IncludeNodeUnittest,
-        grit.node.message_unittest.MessageUnittest,
-        grit.node.misc_unittest.GritNodeUnittest,
-        grit.node.misc_unittest.IfNodeUnittest,
-        grit.node.misc_unittest.ReleaseNodeUnittest,
-        grit.node.node_io_unittest.FileNodeUnittest,
-        grit.node.structure_unittest.StructureUnittest,
-        grit.tool.android2grd_unittest.Android2GrdUnittest,
-        grit.tool.build_unittest.BuildUnittest,
-        grit.tool.buildinfo_unittest.BuildInfoUnittest,
-        grit.tool.postprocess_unittest.PostProcessingUnittest,
-        grit.tool.preprocess_unittest.PreProcessingUnittest,
-        grit.tool.rc2grd_unittest.Rc2GrdUnittest,
-        grit.tool.transl2tc_unittest.TranslationToTcUnittest,
-        grit.tool.xmb_unittest.XmbUnittest,
-        # add test classes here, in alphabetical order...
-    ]
-
-    for test_class in test_classes:
-      self.addTest(unittest.makeSuite(test_class))
 
 def main(args):
-  sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
-                                               '..')))
-  parser = argparse.ArgumentParser(
-      description='Run the full suite of grit unit tests')
-  parser.add_argument(
-      '--write-full-results-to',
-      help='File path that should be used to record the list of test failures')
-  parsed_args = parser.parse_args(args)
-
-  test_result = unittest.TextTestRunner(verbosity=2).run(TestSuiteAll())
-  if (parsed_args.write_full_results_to):
-    failures_and_errors = [str(f[0]) for f in test_result.failures]
-    failures_and_errors.extend(str(e[0]) for e in test_result.errors)
-
-    data = { 'valid': True, 'failures': failures_and_errors }
-    with open(parsed_args.write_full_results_to, 'w') as f:
-      json.dump(data, f)
-
-  return (len(test_result.errors) + len(test_result.failures))
+    return typ.main(
+      top_level_dirs=[os.path.join(CUR_DIR, '..')],
+      skip=['grit.format.gen_predetermined_ids_unittest.*',
+            'grit.pseudo_unittest.*']
+      )
 
 if __name__ == '__main__':
-  sys.exit(main(sys.argv[1:]))
+    sys.exit(main(sys.argv[1:]))
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 71f5907..5d468a66 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -34598,6 +34598,7 @@
   <int value="106840653" label="mus"/>
   <int value="107900612" label="ChromeHomePersistentIph:disabled"/>
   <int value="109577361" label="ForbidSyncXHRInPageDismissal:enabled"/>
+  <int value="112140177" label="DesktopPWAsUnifiedInstall:disabled"/>
   <int value="115915570"
       label="OmniboxUIExperimentHideSteadyStateUrlPathQueryAndRef:enabled"/>
   <int value="116287989"
@@ -35155,6 +35156,7 @@
   <int value="1000706989" label="AutomaticTabDiscarding:disabled"/>
   <int value="1002585107" label="emphasize-titles-in-omnibox-dropdown"/>
   <int value="1003002105" label="MaterialDesignBookmarks:disabled"/>
+  <int value="1003081697" label="DesktopPWAsUnifiedInstall:enabled"/>
   <int value="1004593833" label="LevelDBPerformRewrite:enabled"/>
   <int value="1004909189"
       label="ContentSuggestionsThumbnailDominantColor:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 7eb1e62c..0ae1738 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -136374,7 +136374,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.AsmWasmTranslationThroughput" units="MB/s">
+<histogram name="V8.AsmWasmTranslationThroughput" units="MB/s"
+    expires_after="2020-03-31">
   <owner>mstarzinger@chromium.org</owner>
   <owner>titzer@chromium.org</owner>
   <owner>aseemgarg@chromium.org</owner>
@@ -137665,7 +137666,8 @@
   <summary>TBD</summary>
 </histogram>
 
-<histogram name="V8.WasmAddressSpaceUsageMiB" units="MB">
+<histogram name="V8.WasmAddressSpaceUsageMiB" units="MB"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>ahaas@chromium.org</owner>
@@ -137689,7 +137691,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmCompileFunctionMicroSeconds" units="microseconds">
+<histogram name="V8.WasmCompileFunctionMicroSeconds" units="microseconds"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensh@chromium.org</owner>
@@ -137705,7 +137708,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmCompileFunctionPeakMemoryBytes" units="bytes">
+<histogram name="V8.WasmCompileFunctionPeakMemoryBytes" units="bytes"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensh@chromium.org</owner>
@@ -137715,7 +137719,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmCompileModuleMicroSeconds" units="microseconds">
+<histogram name="V8.WasmCompileModuleMicroSeconds" units="microseconds"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensh@chromium.org</owner>
@@ -137731,7 +137736,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmDecodeFunctionMicroSeconds" units="microseconds">
+<histogram name="V8.WasmDecodeFunctionMicroSeconds" units="microseconds"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensh@chromium.org</owner>
@@ -137747,7 +137753,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmDecodeModuleMicroSeconds" units="microseconds">
+<histogram name="V8.WasmDecodeModuleMicroSeconds" units="microseconds"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensh@chromium.org</owner>
@@ -137763,7 +137770,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmDecodeModulePeakMemoryBytes" units="bytes">
+<histogram name="V8.WasmDecodeModulePeakMemoryBytes" units="bytes"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensh@chromium.org</owner>
@@ -137792,7 +137800,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmFunctionSizeBytes" units="bytes">
+<histogram name="V8.WasmFunctionSizeBytes" units="bytes"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensh@chromium.org</owner>
@@ -137802,7 +137811,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmFunctionsPerModule" units="functions">
+<histogram name="V8.WasmFunctionsPerModule" units="functions"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensh@chromium.org</owner>
@@ -137812,7 +137822,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmInstantiateModuleMicroSeconds" units="microseconds">
+<histogram name="V8.WasmInstantiateModuleMicroSeconds" units="microseconds"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensh@chromium.org</owner>
@@ -137859,7 +137870,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmMaxMemPagesCount" units="pages">
+<histogram name="V8.WasmMaxMemPagesCount" units="pages"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>ahaas@chromium.org</owner>
@@ -137869,7 +137881,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmMemoryAllocationResult" enum="WasmAllocationResult">
+<histogram name="V8.WasmMemoryAllocationResult" enum="WasmAllocationResult"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>ahaas@chromium.org</owner>
@@ -137879,7 +137892,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmMinMemPagesCount" units="pages">
+<histogram name="V8.WasmMinMemPagesCount" units="pages"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>ahaas@chromium.org</owner>
@@ -137956,7 +137970,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.WasmModuleSizeBytes" units="bytes">
+<histogram name="V8.WasmModuleSizeBytes" units="bytes"
+    expires_after="2020-03-31">
   <owner>titzer@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>ahaas@chromium.org</owner>
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 8f22014..3f5eed5 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -3295,6 +3295,7 @@
   g_signal_emit_by_name(G_OBJECT(atk_object_),
                         "property-change::accessible-description",
                         &property_values, nullptr);
+  g_value_unset(&property_values.new_value);
 }
 
 void AXPlatformNodeAuraLinux::OnValueChanged() {
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index cd03aac..6babc24c 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -587,6 +587,12 @@
   <message name="IDS_FILE_BROWSER_CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL" desc="Label for button that changes the view mode to 'thumbnail view' mode.">
     Switch to thumbnail view
   </message>
+  <message name="IDS_FILE_BROWSER_FILE_LIST_CHANGED_TO_LIST_VIEW" desc="Message spoken by ChromeVox/screen reader after clicking on button to switch to list view mode.">
+    File list has changed to list view.
+  </message>
+  <message name="IDS_FILE_BROWSER_FILE_LIST_CHANGED_TO_THUMBNAIL_VIEW" desc="Message spoken by ChromeVox/screen reader after clicking on button to switch to thumbnail view mode.">
+    File list has changed to thumbnail view.
+  </message>
   <message name="IDS_FILE_BROWSER_CLOUD_IMPORT_TITLE" desc="Title of the cloud import feature.">
     Cloud backup
   </message>
@@ -802,6 +808,15 @@
   <message name="IDS_FILE_BROWSER_SEARCH_CLEAR_LABEL" desc="Label to clear search text field">
     Clear
   </message>
+  <message name="IDS_FILE_BROWSER_SEARCH_A11Y_NO_RESULT" desc="Text spoken by ChromeVox/screen reader when a search has no result">
+    There are no results for <ph name="SEARCH_TERM">$1<ex>ABC</ex></ph>.
+  </message>
+  <message name="IDS_FILE_BROWSER_SEARCH_A11Y_RESULT" desc="Text spoken by ChromeVox/screen reader when a search has some result">
+    Showing results for <ph name="SEARCH_TERM">$1<ex>ABC</ex></ph>.
+  </message>
+  <message name="IDS_FILE_BROWSER_SEARCH_A11Y_CLEAR_SEARCH" desc="Text spoken by ChromeVox/screen reader when a search term has been cleared">
+    Search text cleared, showing all files and folders.
+  </message>
 
   <message name="IDS_FILE_BROWSER_DEFAULT_NEW_FOLDER_NAME" desc="The default name for a newly created folder.">
     New folder
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_CHANGE_TO_LISTVIEW_BUTTON_LABEL.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_CHANGE_TO_LISTVIEW_BUTTON_LABEL.png.sha1
new file mode 100644
index 0000000..1f5edbf
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_CHANGE_TO_LISTVIEW_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@
+1903771bd0572e32e61eba8bbc5304ee0d8193a7
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL.png.sha1
new file mode 100644
index 0000000..5a6f9944
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@
+3fecbaec3c25c83dbc73441227e006c8a0d18679
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_A11Y_CLEAR_SEARCH.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_A11Y_CLEAR_SEARCH.png.sha1
new file mode 100644
index 0000000..647619d
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_A11Y_CLEAR_SEARCH.png.sha1
@@ -0,0 +1 @@
+edef63a5a1f1405fbe818071258c46c5172b2de4
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_A11Y_NO_RESULT.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_A11Y_NO_RESULT.png.sha1
new file mode 100644
index 0000000..039133a1
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_A11Y_NO_RESULT.png.sha1
@@ -0,0 +1 @@
+ca79a2a5002f8ef911a49d06f397a925d5c7cfa8
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_A11Y_RESULT.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_A11Y_RESULT.png.sha1
new file mode 100644
index 0000000..42f65cf4
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_A11Y_RESULT.png.sha1
@@ -0,0 +1 @@
+8121b42609d90a9cdd167df030cd0feda886ac4b
\ No newline at end of file
diff --git a/ui/file_manager/file_manager/common/js/metrics_base.js b/ui/file_manager/file_manager/common/js/metrics_base.js
index b78d078..a59cc17a 100644
--- a/ui/file_manager/file_manager/common/js/metrics_base.js
+++ b/ui/file_manager/file_manager/common/js/metrics_base.js
@@ -176,7 +176,7 @@
     'metricName': metrics.convertName_(name),
     'type': chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LINEAR,
     'min': 1,
-    'max': boundaryValue,
+    'max': boundaryValue - 1,
     'buckets': boundaryValue
   };
   metrics.call_('recordValue', [metricDescr, index]);
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 39be149..92522b1 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -1071,7 +1071,7 @@
     // Create search controller.
     this.searchController_ = new SearchController(
         this.ui_.searchBox, assert(this.ui_.locationLine), this.directoryModel_,
-        this.volumeManager_, assert(this.taskController_));
+        this.volumeManager_, assert(this.taskController_), assert(this.ui_));
 
     // Create directory tree naming controller.
     this.directoryTreeNamingController_ = new DirectoryTreeNamingController(
diff --git a/ui/file_manager/file_manager/foreground/js/main_window_component.js b/ui/file_manager/file_manager/foreground/js/main_window_component.js
index 7c109b9..5819e35 100644
--- a/ui/file_manager/file_manager/foreground/js/main_window_component.js
+++ b/ui/file_manager/file_manager/foreground/js/main_window_component.js
@@ -292,6 +292,10 @@
       ListContainer.ListType.THUMBNAIL :
       ListContainer.ListType.DETAIL;
   this.ui_.setCurrentListType(listType);
+  const msgId = listType === ListContainer.ListType.DETAIL ?
+      'FILE_LIST_CHANGED_TO_LIST_VIEW' :
+      'FILE_LIST_CHANGED_TO_LIST_THUMBNAIL_VIEW';
+  this.ui_.speakA11yMessage(str(msgId));
   this.appStateController_.saveViewOptions();
 
   this.ui_.listContainer.focus();
diff --git a/ui/file_manager/file_manager/foreground/js/search_controller.js b/ui/file_manager/file_manager/foreground/js/search_controller.js
index c0986c7..944460d 100644
--- a/ui/file_manager/file_manager/foreground/js/search_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/search_controller.js
@@ -12,9 +12,12 @@
    * @param {!DirectoryModel} directoryModel Directory model.
    * @param {!TaskController} taskController Task controller to execute the
    *     selected item.
+   * @param {!FileManagerUI} a11y FileManagerUI to be able to announce a11y
+   *     messages.
    */
   constructor(
-      searchBox, locationLine, directoryModel, volumeManager, taskController) {
+      searchBox, locationLine, directoryModel, volumeManager, taskController,
+      a11y) {
     /** @const @private {!SearchBox} */
     this.searchBox_ = searchBox;
 
@@ -36,6 +39,9 @@
     /** @private {boolean} */
     this.autocompleteSuggestionsBusy_ = false;
 
+    /** @const @private {!FileManagerUI} */
+    this.a11y_ = a11y;
+
     searchBox.addEventListener(
         SearchBox.EventType.TEXT_CHANGE, this.onTextChange_.bind(this));
     searchBox.addEventListener(
@@ -122,6 +128,8 @@
 
     // Clear search if the query empty.
     if (!searchString) {
+      const msg = str('SEARCH_A11Y_CLEAR_SEARCH');
+      this.a11y_.speakA11yMessage(msg);
       this.searchBox_.autocompleteList.suggestions = [];
       return;
     }
@@ -229,7 +237,18 @@
    * @private
    */
   search_(searchString) {
+    if (!searchString) {
+      const msg = str('SEARCH_A11Y_CLEAR_SEARCH');
+      this.a11y_.speakA11yMessage(msg);
+    }
     const onSearchRescan = function() {
+      const fileList = this.directoryModel_.getFileList();
+      const count = fileList.getFileCount() + fileList.getFolderCount();
+      const msgId =
+          count === 0 ? 'SEARCH_A11Y_NO_RESULT' : 'SEARCH_A11Y_RESULT';
+      const msg = strf(msgId, searchString);
+      this.a11y_.speakA11yMessage(msg);
+
       // If the current location is somewhere in Drive, all files in Drive can
       // be listed as search results regardless of current location.
       // In this case, showing current location is confusing, so use the Drive
diff --git a/ui/file_manager/gallery/js/image_editor/image_editor.js b/ui/file_manager/gallery/js/image_editor/image_editor.js
index 8a45e5e..d4891f3 100644
--- a/ui/file_manager/gallery/js/image_editor/image_editor.js
+++ b/ui/file_manager/gallery/js/image_editor/image_editor.js
@@ -5,719 +5,911 @@
 /**
  * ImageEditor is the top level object that holds together and connects
  * everything needed for image editing.
- *
- * @param {!Viewport} viewport The viewport.
- * @param {!ImageView} imageView The ImageView containing the images to edit.
- * @param {!ImageEditorPrompt} prompt Prompt instance.
- * @param {!{image: !HTMLElement, root: !HTMLElement, toolbar: !HTMLElement,
- * mode: !HTMLElement}} DOMContainers Various DOM containers required for the
- *     editor.
- * @param {!Array<!ImageEditorMode>} modes Available editor modes.
- * @param {function(string, ...string)} displayStringFunction String
- *     formatting function.
- * @constructor
- * @extends {cr.EventTarget}
- * @struct
- *
- * TODO(yawano): Remove displayStringFunction from arguments.
  */
-function ImageEditor(
-    viewport, imageView, prompt, DOMContainers, modes, displayStringFunction) {
-  cr.EventTarget.call(this);
-
-  this.rootContainer_ = DOMContainers.root;
-  this.container_ = DOMContainers.image;
-  this.modes_ = modes;
-  this.displayStringFunction_ = displayStringFunction;
-
+class ImageEditor extends cr.EventTarget {
   /**
-   * @private {ImageEditorMode}
+   * @param {!Viewport} viewport The viewport.
+   * @param {!ImageView} imageView The ImageView containing the images to edit.
+   * @param {!ImageEditorPrompt} prompt Prompt instance.
+   * @param {!{image: !HTMLElement, root: !HTMLElement, toolbar: !HTMLElement,
+   * mode: !HTMLElement}} DOMContainers Various DOM containers required for the
+   *     editor.
+   * @param {!Array<!ImageEditorMode>} modes Available editor modes.
+   * @param {function(string, ...string)} displayStringFunction String
+   *     formatting function.
+   *
+   * TODO(yawano): Remove displayStringFunction from arguments.
    */
-  this.currentMode_ = null;
+  constructor(
+      viewport, imageView, prompt, DOMContainers, modes,
+      displayStringFunction) {
+    super();
 
-  /**
-   * @private {HTMLElement}
-   */
-  this.currentTool_ = null;
+    this.rootContainer_ = DOMContainers.root;
+    this.container_ = DOMContainers.image;
+    this.modes_ = modes;
+    this.displayStringFunction_ = displayStringFunction;
 
-  /**
-   * @private {boolean}
-   */
-  this.settingUpNextMode_ = false;
+    /**
+     * @private {ImageEditorMode}
+     */
+    this.currentMode_ = null;
 
-  ImageUtil.removeChildren(this.container_);
+    /**
+     * @private {HTMLElement}
+     */
+    this.currentTool_ = null;
 
-  this.viewport_ = viewport;
-
-  this.imageView_ = imageView;
-
-  this.buffer_ = new ImageBuffer();
-  this.buffer_.addOverlay(this.imageView_);
-
-  this.panControl_ = new ImageEditor.MouseControl(
-      this.rootContainer_, this.container_, this.getBuffer());
-  this.panControl_.setDoubleTapCallback(this.onDoubleTap_.bind(this));
-
-  this.mainToolbar_ =
-      new ImageEditorToolbar(DOMContainers.toolbar, displayStringFunction);
-
-  this.modeToolbar_ = new ImageEditorToolbar(
-      DOMContainers.mode, displayStringFunction,
-      this.onOptionsChange.bind(this), true /* done button */);
-  this.modeToolbar_.addEventListener(
-      'done-clicked', this.onDoneClicked_.bind(this));
-  this.modeToolbar_.addEventListener(
-      'cancel-clicked', this.onCancelClicked_.bind(this));
-
-  this.prompt_ = prompt;
-
-  this.commandQueue_ = null;
-
-  // -----------------------------------------------------------------
-  // Populate the toolbar.
-
-  /**
-   * @type {!Array<string>}
-   * @private
-   */
-  this.actionNames_ = [];
-
-  this.mainToolbar_.clear();
-
-  // Create action buttons.
-  for (var i = 0; i != this.modes_.length; i++) {
-    var mode = this.modes_[i];
-    var button = this.createToolButton_(
-        mode.name, mode.title, this.enterMode.bind(this, mode), mode.instant);
-    mode.bind(
-        button, this.getBuffer(), this.getViewport(), this.getImageView());
-    this.registerAction_(mode.name);
-  }
-
-  /**
-   * @type {!HTMLElement}
-   * @private
-   */
-  this.undoButton_ = this.createToolButton_('undo', 'GALLERY_UNDO',
-      this.undo.bind(this),
-      true /* instant */);
-  this.registerAction_('undo');
-
-  /**
-   * @type {!HTMLElement}
-   * @private
-   */
-  this.redoButton_ = this.createToolButton_('redo', 'GALLERY_REDO',
-      this.redo.bind(this),
-      true /* instant */);
-  this.registerAction_('redo');
-
-  /**
-   * @private {!HTMLElement}
-   * @const
-   */
-  this.exitButton_ = /** @type {!HTMLElement} */
-      (queryRequiredElement('.edit-mode-toolbar paper-button.exit'));
-  this.exitButton_.addEventListener('click', this.onExitClicked_.bind(this));
-
-  /**
-   * @private {!FilesToast}
-   */
-  this.filesToast_ = /** @type {!FilesToast}*/
-      (queryRequiredElement('files-toast'));
-}
-
-ImageEditor.prototype.__proto__ = cr.EventTarget.prototype;
-
-/**
- * Handles click event of exit button.
- * @private
- */
-ImageEditor.prototype.onExitClicked_ = function() {
-  var event = new Event('exit-clicked');
-  this.dispatchEvent(event);
-};
-
-/**
- * Creates a toolbar button.
- * @param {string} name Button name.
- * @param {string} title Button title.
- * @param {function(Event)} handler onClick handler.
- * @param {boolean} isInstant True if this tool (mode) is instant.
- * @return {!HTMLElement} A created button.
- * @private
- */
-ImageEditor.prototype.createToolButton_ = function(
-    name, title, handler, isInstant) {
-  var button = this.mainToolbar_.addButton(
-      title,
-      isInstant ? ImageEditorToolbar.ButtonType.ICON :
-                  ImageEditorToolbar.ButtonType.ICON_TOGGLEABLE,
-      handler, name /* opt_className */);
-  return button;
-};
-
-/**
- * @return {boolean} True if no user commands are to be accepted.
- */
-ImageEditor.prototype.isLocked = function() {
-  return !this.commandQueue_ || this.commandQueue_.isBusy();
-};
-
-/**
- * @return {boolean} True if the command queue is busy.
- */
-ImageEditor.prototype.isBusy = function() {
-  return this.commandQueue_ && this.commandQueue_.isBusy();
-};
-
-/**
- * Reflect the locked state of the editor in the UI.
- * @param {boolean} on True if locked.
- */
-ImageEditor.prototype.lockUI = function(on) {
-  ImageUtil.setAttribute(this.rootContainer_, 'locked', on);
-};
-
-/**
- * Report the tool use to the metrics subsystem.
- * @param {string} name Action name.
- */
-ImageEditor.prototype.recordToolUse = function(name) {
-  metrics.recordEnum(
-      ImageUtil.getMetricName('Tool'), name, this.actionNames_);
-};
-
-/**
- * Content update handler.
- * @private
- */
-ImageEditor.prototype.calculateModeApplicativity_ = function() {
-  for (var i = 0; i != this.modes_.length; i++) {
-    var mode = this.modes_[i];
-    ImageUtil.setAttribute(assert(mode.button_), 'disabled',
-        !mode.isApplicable());
-  }
-};
-
-/**
- * Open the editing session for a new image.
- *
- * @param {!GalleryItem} item Gallery item.
- * @param {!ImageView.Effect} effect Transition effect object.
- * @param {function(function())} saveFunction Image save function.
- * @param {function()} displayCallback Display callback.
- * @param {function(!ImageView.LoadType, number, *=)} loadCallback Load
- *     callback.
- */
-ImageEditor.prototype.openSession = function(
-    item, effect, saveFunction, displayCallback, loadCallback) {
-  if (this.commandQueue_) {
-    throw new Error('Session not closed');
-  }
-
-  this.lockUI(true);
-
-  var self = this;
-  this.imageView_.load(
-      item, effect, displayCallback, function(loadType, delay, error) {
-        self.lockUI(false);
-
-        // Always handle an item as original for new session.
-        item.setAsOriginal();
-
-        self.commandQueue_ = new CommandQueue(
-            assert(self.container_.ownerDocument),
-            assert(self.imageView_.getEditableImage()), saveFunction);
-        self.commandQueue_.attachUI(
-            self.getImageView(), self.getPrompt(), self.filesToast_,
-            self.updateUndoRedo.bind(self), self.lockUI.bind(self));
-        self.updateUndoRedo();
-        loadCallback(loadType, delay, error);
-      });
-};
-
-/**
- * Close the current image editing session.
- * @param {function()} callback Callback.
- */
-ImageEditor.prototype.closeSession = function(callback) {
-  this.getPrompt().hide();
-  if (this.imageView_.isLoading()) {
-    if (this.commandQueue_) {
-      console.warn('Inconsistent image editor state');
-      this.commandQueue_ = null;
-    }
-    this.imageView_.cancelLoad();
-    this.lockUI(false);
-    callback();
-    return;
-  }
-  if (!this.commandQueue_) {
-    // Session is already closed.
-    callback();
-    return;
-  }
-
-  this.executeWhenReady(callback);
-  this.commandQueue_.close();
-  this.commandQueue_ = null;
-};
-
-/**
- * Commit the current operation and execute the action.
- *
- * @param {function()} callback Callback.
- */
-ImageEditor.prototype.executeWhenReady = function(callback) {
-  if (this.commandQueue_) {
-    this.leaveMode(false /* not to switch mode */);
-    this.commandQueue_.executeWhenReady(callback);
-  } else {
-    if (!this.imageView_.isLoading()) {
-      console.warn('Inconsistent image editor state');
-    }
-    callback();
-  }
-};
-
-/**
- * @return {boolean} True if undo queue is not empty.
- */
-ImageEditor.prototype.canUndo = function() {
-  return !!this.commandQueue_ && this.commandQueue_.canUndo();
-};
-
-/**
- * Undo the recently executed command.
- */
-ImageEditor.prototype.undo = function() {
-  if (this.isLocked()) return;
-  this.recordToolUse('undo');
-
-  // First undo click should dismiss the uncommitted modifications.
-  if (this.currentMode_ && this.currentMode_.isUpdated()) {
-    this.modeToolbar_.reset();
-    this.currentMode_.reset();
-    return;
-  }
-
-  this.getPrompt().hide();
-  this.leaveModeInternal_(false, false /* not to switch mode */);
-  this.commandQueue_.undo();
-  this.updateUndoRedo();
-  this.calculateModeApplicativity_();
-};
-
-/**
- * Redo the recently un-done command.
- */
-ImageEditor.prototype.redo = function() {
-  if (this.isLocked()) return;
-  this.recordToolUse('redo');
-  this.getPrompt().hide();
-  this.leaveModeInternal_(false, false /* not to switch mode */);
-  this.commandQueue_.redo();
-  this.updateUndoRedo();
-  this.calculateModeApplicativity_();
-};
-
-/**
- * Update Undo/Redo buttons state.
- */
-ImageEditor.prototype.updateUndoRedo = function() {
-  var canUndo = this.commandQueue_ && this.commandQueue_.canUndo();
-  var canRedo = this.commandQueue_ && this.commandQueue_.canRedo();
-  ImageUtil.setAttribute(this.undoButton_, 'disabled', !canUndo);
-  ImageUtil.setAttribute(this.redoButton_, 'disabled', !canRedo);
-};
-
-/**
- * @return {HTMLCanvasElement|HTMLImageElement} The current image.
- */
-ImageEditor.prototype.getImage = function() {
-  return this.getImageView().getEditableImage();
-};
-
-/**
- * @return {!ImageBuffer} ImageBuffer instance.
- */
-ImageEditor.prototype.getBuffer = function() {
-  return this.buffer_;
-};
-
-/**
- * @return {!ImageView} ImageView instance.
- */
-ImageEditor.prototype.getImageView = function() {
-  return this.imageView_;
-};
-
-/**
- * @return {!Viewport} Viewport instance.
- */
-ImageEditor.prototype.getViewport = function() {
-  return this.viewport_;
-};
-
-/**
- * @return {!ImageEditorPrompt} Prompt instance.
- */
-ImageEditor.prototype.getPrompt = function() {
-  return this.prompt_;
-};
-
-/**
- * Handle the toolbar controls update.
- * @param {Object} options A map of options.
- */
-ImageEditor.prototype.onOptionsChange = function(options) {
-  ImageUtil.trace.resetTimer('update');
-  if (this.currentMode_) {
-    this.currentMode_.update(options);
-  }
-  ImageUtil.trace.reportTimer('update');
-};
-
-/**
- * Register the action name. Required for metrics reporting.
- * @param {string} name Button name.
- * @private
- */
-ImageEditor.prototype.registerAction_ = function(name) {
-  this.actionNames_.push(name);
-};
-
-/**
- * @return {ImageEditorMode} The current mode.
- */
-ImageEditor.prototype.getMode = function() {
-  return this.currentMode_;
-};
-
-/**
- * The user clicked on the mode button.
- *
- * @param {!ImageEditorMode} mode The new mode.
- */
-ImageEditor.prototype.enterMode = function(mode) {
-  if (this.isLocked()) return;
-
-  if (this.currentMode_ === mode) {
-    // Currently active editor tool clicked, commit if modified.
-    this.leaveModeInternal_(
-        this.currentMode_.updated_, false /* not to switch mode */);
-    return;
-  }
-
-  // Guard not to call setUpMode_ more than once.
-  if (this.settingUpNextMode_) {
-    return;
-  }
-  this.settingUpNextMode_ = true;
-
-  this.recordToolUse(mode.name);
-
-  this.leaveMode(true /* to switch mode */);
-
-  // The above call could have caused a commit which might have initiated
-  // an asynchronous command execution. Wait for it to complete, then proceed
-  // with the mode set up.
-  this.commandQueue_.executeWhenReady(function() {
-    this.setUpMode_(mode);
+    /**
+     * @private {boolean}
+     */
     this.settingUpNextMode_ = false;
-  }.bind(this));
-};
 
-/**
- * Set up the new editing mode.
- *
- * @param {!ImageEditorMode} mode The mode.
- * @private
- */
-ImageEditor.prototype.setUpMode_ = function(mode) {
-  this.currentTool_ = mode.button_;
-  this.currentMode_ = mode;
-  this.rootContainer_.setAttribute('editor-mode', mode.name);
+    ImageUtil.removeChildren(this.container_);
 
-  // Activate toggle ripple if button is toggleable.
-  var filesToggleRipple =
-      this.currentTool_.querySelector('files-toggle-ripple');
-  if (filesToggleRipple) {
-    // Current mode must NOT be instant for toggleable button.
-    assert(!this.currentMode_.instant);
-    filesToggleRipple.activated = true;
+    this.viewport_ = viewport;
+
+    this.imageView_ = imageView;
+
+    this.buffer_ = new ImageBuffer();
+    this.buffer_.addOverlay(this.imageView_);
+
+    this.panControl_ = new ImageEditor.MouseControl(
+        this.rootContainer_, this.container_, this.getBuffer());
+    this.panControl_.setDoubleTapCallback(this.onDoubleTap_.bind(this));
+
+    this.mainToolbar_ =
+        new ImageEditorToolbar(DOMContainers.toolbar, displayStringFunction);
+
+    this.modeToolbar_ = new ImageEditorToolbar(
+        DOMContainers.mode, displayStringFunction,
+        this.onOptionsChange.bind(this), true /* done button */);
+    this.modeToolbar_.addEventListener(
+        'done-clicked', this.onDoneClicked_.bind(this));
+    this.modeToolbar_.addEventListener(
+        'cancel-clicked', this.onCancelClicked_.bind(this));
+
+    this.prompt_ = prompt;
+
+    this.commandQueue_ = null;
+
+    // -----------------------------------------------------------------
+    // Populate the toolbar.
+
+    /**
+     * @type {!Array<string>}
+     * @private
+     */
+    this.actionNames_ = [];
+
+    this.mainToolbar_.clear();
+
+    // Create action buttons.
+    for (var i = 0; i != this.modes_.length; i++) {
+      var mode = this.modes_[i];
+      var button = this.createToolButton_(
+          mode.name, mode.title, this.enterMode.bind(this, mode), mode.instant);
+      mode.bind(
+          button, this.getBuffer(), this.getViewport(), this.getImageView());
+      this.registerAction_(mode.name);
+    }
+
+    /**
+     * @type {!HTMLElement}
+     * @private
+     */
+    this.undoButton_ = this.createToolButton_(
+        'undo', 'GALLERY_UNDO', this.undo.bind(this), true /* instant */);
+    this.registerAction_('undo');
+
+    /**
+     * @type {!HTMLElement}
+     * @private
+     */
+    this.redoButton_ = this.createToolButton_(
+        'redo', 'GALLERY_REDO', this.redo.bind(this), true /* instant */);
+    this.registerAction_('redo');
+
+    /**
+     * @private {!HTMLElement}
+     * @const
+     */
+    this.exitButton_ = /** @type {!HTMLElement} */
+        (queryRequiredElement('.edit-mode-toolbar paper-button.exit'));
+    this.exitButton_.addEventListener('click', this.onExitClicked_.bind(this));
+
+    /**
+     * @private {!FilesToast}
+     */
+    this.filesToast_ = /** @type {!FilesToast}*/
+        (queryRequiredElement('files-toast'));
   }
 
-  // Scale the screen so that it doesn't overlap the toolbars. We should scale
-  // the screen before setup of current mode is called to make the current mode
-  // able to set up with new screen size.
-  if (!this.currentMode_.instant) {
-    this.getViewport().setScreenTop(
-        ImageEditorToolbar.HEIGHT + mode.paddingTop);
-    this.getViewport().setScreenBottom(
-        ImageEditorToolbar.HEIGHT * 2 + mode.paddingBottom);
-    this.getImageView().applyViewportChange();
+  /**
+   * Handles click event of exit button.
+   * @private
+   */
+  onExitClicked_() {
+    var event = new Event('exit-clicked');
+    this.dispatchEvent(event);
   }
 
-  this.currentMode_.setUp();
-
-  this.calculateModeApplicativity_();
-  if (this.currentMode_.instant) {  // Instant tool.
-    this.leaveModeInternal_(true, false /* not to switch mode */);
-    return;
+  /**
+   * Creates a toolbar button.
+   * @param {string} name Button name.
+   * @param {string} title Button title.
+   * @param {function(Event)} handler onClick handler.
+   * @param {boolean} isInstant True if this tool (mode) is instant.
+   * @return {!HTMLElement} A created button.
+   * @private
+   */
+  createToolButton_(name, title, handler, isInstant) {
+    var button = this.mainToolbar_.addButton(
+        title,
+        isInstant ? ImageEditorToolbar.ButtonType.ICON :
+                    ImageEditorToolbar.ButtonType.ICON_TOGGLEABLE,
+        handler, name /* opt_className */);
+    return button;
   }
 
-  this.exitButton_.hidden = true;
-
-  this.modeToolbar_.clear();
-  this.currentMode_.createTools(this.modeToolbar_);
-  this.modeToolbar_.show(true);
-};
-
-/**
- * Handles click event of Done button.
- * @param {!Event} event An event.
- * @private
- */
-ImageEditor.prototype.onDoneClicked_ = function(event) {
-  this.leaveModeInternal_(true /* commit */, false /* not to switch mode */);
-};
-
-/**
- * Handles click event of Cancel button.
- * @param {!Event} event An event.
- * @private
- */
-ImageEditor.prototype.onCancelClicked_ = function(event) {
-  this.leaveModeInternal_(
-      false /* not commit */, false /* not to switch mode */);
-};
-
-/**
- * The user clicked on 'OK' or 'Cancel' or on a different mode button.
- * @param {boolean} commit True if commit is required.
- * @param {boolean} leaveToSwitchMode True if it leaves to change mode.
- * @private
- */
-ImageEditor.prototype.leaveModeInternal_ = function(commit, leaveToSwitchMode) {
-  if (!this.currentMode_) {
-    return;
+  /**
+   * @return {boolean} True if no user commands are to be accepted.
+   */
+  isLocked() {
+    return !this.commandQueue_ || this.commandQueue_.isBusy();
   }
 
-  // If the current mode is 'Resize', and commit is required,
-  // leaving mode should be stopped when an input value is not valid.
-  if(commit && this.currentMode_.name === 'resize') {
-    var resizeMode = /** @type {!ImageEditorMode.Resize} */
-        (this.currentMode_);
-    if(!resizeMode.isInputValid()) {
-      resizeMode.showAlertDialog();
-      return;
+  /**
+   * @return {boolean} True if the command queue is busy.
+   */
+  isBusy() {
+    return this.commandQueue_ && this.commandQueue_.isBusy();
+  }
+
+  /**
+   * Reflect the locked state of the editor in the UI.
+   * @param {boolean} on True if locked.
+   */
+  lockUI(on) {
+    ImageUtil.setAttribute(this.rootContainer_, 'locked', on);
+  }
+
+  /**
+   * Report the tool use to the metrics subsystem.
+   * @param {string} name Action name.
+   */
+  recordToolUse(name) {
+    metrics.recordEnum(
+        ImageUtil.getMetricName('Tool'), name, this.actionNames_);
+  }
+
+  /**
+   * Content update handler.
+   * @private
+   */
+  calculateModeApplicativity_() {
+    for (var i = 0; i != this.modes_.length; i++) {
+      var mode = this.modes_[i];
+      ImageUtil.setAttribute(
+          assert(mode.button_), 'disabled', !mode.isApplicable());
     }
   }
 
-  this.modeToolbar_.show(false);
-  this.rootContainer_.removeAttribute('editor-mode');
+  /**
+   * Open the editing session for a new image.
+   *
+   * @param {!GalleryItem} item Gallery item.
+   * @param {!ImageView.Effect} effect Transition effect object.
+   * @param {function(function())} saveFunction Image save function.
+   * @param {function()} displayCallback Display callback.
+   * @param {function(!ImageView.LoadType, number, *=)} loadCallback Load
+   *     callback.
+   */
+  openSession(item, effect, saveFunction, displayCallback, loadCallback) {
+    if (this.commandQueue_) {
+      throw new Error('Session not closed');
+    }
 
-  // If it leaves to switch mode, do not restore screen size since the next mode
-  // might change screen size. We should avoid to show intermediate animation
-  // which tries to restore screen size.
-  if (!leaveToSwitchMode) {
-    this.getViewport().setScreenTop(ImageEditorToolbar.HEIGHT);
-    this.getViewport().setScreenBottom(ImageEditorToolbar.HEIGHT);
-    this.getImageView().applyViewportChange();
-  }
+    this.lockUI(true);
 
-  this.currentMode_.cleanUpUI();
-
-  if (commit) {
     var self = this;
-    var command = this.currentMode_.getCommand();
-    if (command) {  // Could be null if the user did not do anything.
-      this.commandQueue_.execute(command);
-      this.updateUndoRedo();
-    }
+    this.imageView_.load(
+        item, effect, displayCallback, function(loadType, delay, error) {
+          self.lockUI(false);
+
+          // Always handle an item as original for new session.
+          item.setAsOriginal();
+
+          self.commandQueue_ = new CommandQueue(
+              assert(self.container_.ownerDocument),
+              assert(self.imageView_.getEditableImage()), saveFunction);
+          self.commandQueue_.attachUI(
+              self.getImageView(), self.getPrompt(), self.filesToast_,
+              self.updateUndoRedo.bind(self), self.lockUI.bind(self));
+          self.updateUndoRedo();
+          loadCallback(loadType, delay, error);
+        });
   }
 
-  var filesToggleRipple =
-      this.currentTool_.querySelector('files-toggle-ripple');
-  if (filesToggleRipple) {
-    filesToggleRipple.activated = false;
-  }
-
-  this.exitButton_.hidden = false;
-
-  this.currentMode_.cleanUpCaches();
-  this.currentMode_ = null;
-  this.currentTool_ = null;
-};
-
-/**
- * Leave the mode, commit only if required by the current mode.
- * @param {boolean} leaveToSwitchMode True if it leaves to switch mode.
- */
-ImageEditor.prototype.leaveMode = function(leaveToSwitchMode) {
-  this.leaveModeInternal_(!!this.currentMode_ &&
-      this.currentMode_.updated_ &&
-      this.currentMode_.implicitCommit,
-      leaveToSwitchMode);
-};
-
-/**
- * Enter the editor mode with the given name.
- *
- * @param {string} name Mode name.
- * @private
- */
-ImageEditor.prototype.enterModeByName_ = function(name) {
-  for (var i = 0; i !== this.modes_.length; i++) {
-    var mode = this.modes_[i];
-    if (mode.name === name) {
-      if (!mode.button_.hasAttribute('disabled')) {
-        this.enterMode(mode);
+  /**
+   * Close the current image editing session.
+   * @param {function()} callback Callback.
+   */
+  closeSession(callback) {
+    this.getPrompt().hide();
+    if (this.imageView_.isLoading()) {
+      if (this.commandQueue_) {
+        console.warn('Inconsistent image editor state');
+        this.commandQueue_ = null;
       }
+      this.imageView_.cancelLoad();
+      this.lockUI(false);
+      callback();
       return;
     }
-  }
-  console.error('Mode "' + name + '" not found.');
-};
+    if (!this.commandQueue_) {
+      // Session is already closed.
+      callback();
+      return;
+    }
 
-/**
- * Key down handler.
- * @param {!Event} event The keydown event.
- * @return {boolean} True if handled.
- */
-ImageEditor.prototype.onKeyDown = function(event) {
-  if (this.currentMode_ && this.currentMode_.isConsumingKeyEvents()) {
+    this.executeWhenReady(callback);
+    this.commandQueue_.close();
+    this.commandQueue_ = null;
+  }
+
+  /**
+   * Commit the current operation and execute the action.
+   *
+   * @param {function()} callback Callback.
+   */
+  executeWhenReady(callback) {
+    if (this.commandQueue_) {
+      this.leaveMode(false /* not to switch mode */);
+      this.commandQueue_.executeWhenReady(callback);
+    } else {
+      if (!this.imageView_.isLoading()) {
+        console.warn('Inconsistent image editor state');
+      }
+      callback();
+    }
+  }
+
+  /**
+   * @return {boolean} True if undo queue is not empty.
+   */
+  canUndo() {
+    return !!this.commandQueue_ && this.commandQueue_.canUndo();
+  }
+
+  /**
+   * Undo the recently executed command.
+   */
+  undo() {
+    if (this.isLocked()) {
+      return;
+    }
+    this.recordToolUse('undo');
+
+    // First undo click should dismiss the uncommitted modifications.
+    if (this.currentMode_ && this.currentMode_.isUpdated()) {
+      this.modeToolbar_.reset();
+      this.currentMode_.reset();
+      return;
+    }
+
+    this.getPrompt().hide();
+    this.leaveModeInternal_(false, false /* not to switch mode */);
+    this.commandQueue_.undo();
+    this.updateUndoRedo();
+    this.calculateModeApplicativity_();
+  }
+
+  /**
+   * Redo the recently un-done command.
+   */
+  redo() {
+    if (this.isLocked()) {
+      return;
+    }
+    this.recordToolUse('redo');
+    this.getPrompt().hide();
+    this.leaveModeInternal_(false, false /* not to switch mode */);
+    this.commandQueue_.redo();
+    this.updateUndoRedo();
+    this.calculateModeApplicativity_();
+  }
+
+  /**
+   * Update Undo/Redo buttons state.
+   */
+  updateUndoRedo() {
+    var canUndo = this.commandQueue_ && this.commandQueue_.canUndo();
+    var canRedo = this.commandQueue_ && this.commandQueue_.canRedo();
+    ImageUtil.setAttribute(this.undoButton_, 'disabled', !canUndo);
+    ImageUtil.setAttribute(this.redoButton_, 'disabled', !canRedo);
+  }
+
+  /**
+   * @return {HTMLCanvasElement|HTMLImageElement} The current image.
+   */
+  getImage() {
+    return this.getImageView().getEditableImage();
+  }
+
+  /**
+   * @return {!ImageBuffer} ImageBuffer instance.
+   */
+  getBuffer() {
+    return this.buffer_;
+  }
+
+  /**
+   * @return {!ImageView} ImageView instance.
+   */
+  getImageView() {
+    return this.imageView_;
+  }
+
+  /**
+   * @return {!Viewport} Viewport instance.
+   */
+  getViewport() {
+    return this.viewport_;
+  }
+
+  /**
+   * @return {!ImageEditorPrompt} Prompt instance.
+   */
+  getPrompt() {
+    return this.prompt_;
+  }
+
+  /**
+   * Handle the toolbar controls update.
+   * @param {Object} options A map of options.
+   */
+  onOptionsChange(options) {
+    ImageUtil.trace.resetTimer('update');
+    if (this.currentMode_) {
+      this.currentMode_.update(options);
+    }
+    ImageUtil.trace.reportTimer('update');
+  }
+
+  /**
+   * Register the action name. Required for metrics reporting.
+   * @param {string} name Button name.
+   * @private
+   */
+  registerAction_(name) {
+    this.actionNames_.push(name);
+  }
+
+  /**
+   * @return {ImageEditorMode} The current mode.
+   */
+  getMode() {
+    return this.currentMode_;
+  }
+
+  /**
+   * The user clicked on the mode button.
+   *
+   * @param {!ImageEditorMode} mode The new mode.
+   */
+  enterMode(mode) {
+    if (this.isLocked()) {
+      return;
+    }
+
+    if (this.currentMode_ === mode) {
+      // Currently active editor tool clicked, commit if modified.
+      this.leaveModeInternal_(
+          this.currentMode_.updated_, false /* not to switch mode */);
+      return;
+    }
+
+    // Guard not to call setUpMode_ more than once.
+    if (this.settingUpNextMode_) {
+      return;
+    }
+    this.settingUpNextMode_ = true;
+
+    this.recordToolUse(mode.name);
+
+    this.leaveMode(true /* to switch mode */);
+
+    // The above call could have caused a commit which might have initiated
+    // an asynchronous command execution. Wait for it to complete, then proceed
+    // with the mode set up.
+    this.commandQueue_.executeWhenReady(function() {
+      this.setUpMode_(mode);
+      this.settingUpNextMode_ = false;
+    }.bind(this));
+  }
+
+  /**
+   * Set up the new editing mode.
+   *
+   * @param {!ImageEditorMode} mode The mode.
+   * @private
+   */
+  setUpMode_(mode) {
+    this.currentTool_ = mode.button_;
+    this.currentMode_ = mode;
+    this.rootContainer_.setAttribute('editor-mode', mode.name);
+
+    // Activate toggle ripple if button is toggleable.
+    var filesToggleRipple =
+        this.currentTool_.querySelector('files-toggle-ripple');
+    if (filesToggleRipple) {
+      // Current mode must NOT be instant for toggleable button.
+      assert(!this.currentMode_.instant);
+      filesToggleRipple.activated = true;
+    }
+
+    // Scale the screen so that it doesn't overlap the toolbars. We should scale
+    // the screen before setup of current mode is called to make the current
+    // mode able to set up with new screen size.
+    if (!this.currentMode_.instant) {
+      this.getViewport().setScreenTop(
+          ImageEditorToolbar.HEIGHT + mode.paddingTop);
+      this.getViewport().setScreenBottom(
+          ImageEditorToolbar.HEIGHT * 2 + mode.paddingBottom);
+      this.getImageView().applyViewportChange();
+    }
+
+    this.currentMode_.setUp();
+
+    this.calculateModeApplicativity_();
+    if (this.currentMode_.instant) {  // Instant tool.
+      this.leaveModeInternal_(true, false /* not to switch mode */);
+      return;
+    }
+
+    this.exitButton_.hidden = true;
+
+    this.modeToolbar_.clear();
+    this.currentMode_.createTools(this.modeToolbar_);
+    this.modeToolbar_.show(true);
+  }
+
+  /**
+   * Handles click event of Done button.
+   * @param {!Event} event An event.
+   * @private
+   */
+  onDoneClicked_(event) {
+    this.leaveModeInternal_(true /* commit */, false /* not to switch mode */);
+  }
+
+  /**
+   * Handles click event of Cancel button.
+   * @param {!Event} event An event.
+   * @private
+   */
+  onCancelClicked_(event) {
+    this.leaveModeInternal_(
+        false /* not commit */, false /* not to switch mode */);
+  }
+
+  /**
+   * The user clicked on 'OK' or 'Cancel' or on a different mode button.
+   * @param {boolean} commit True if commit is required.
+   * @param {boolean} leaveToSwitchMode True if it leaves to change mode.
+   * @private
+   */
+  leaveModeInternal_(commit, leaveToSwitchMode) {
+    if (!this.currentMode_) {
+      return;
+    }
+
+    // If the current mode is 'Resize', and commit is required,
+    // leaving mode should be stopped when an input value is not valid.
+    if (commit && this.currentMode_.name === 'resize') {
+      var resizeMode = /** @type {!ImageEditorMode.Resize} */
+          (this.currentMode_);
+      if (!resizeMode.isInputValid()) {
+        resizeMode.showAlertDialog();
+        return;
+      }
+    }
+
+    this.modeToolbar_.show(false);
+    this.rootContainer_.removeAttribute('editor-mode');
+
+    // If it leaves to switch mode, do not restore screen size since the next
+    // mode might change screen size. We should avoid to show intermediate
+    // animation which tries to restore screen size.
+    if (!leaveToSwitchMode) {
+      this.getViewport().setScreenTop(ImageEditorToolbar.HEIGHT);
+      this.getViewport().setScreenBottom(ImageEditorToolbar.HEIGHT);
+      this.getImageView().applyViewportChange();
+    }
+
+    this.currentMode_.cleanUpUI();
+
+    if (commit) {
+      var self = this;
+      var command = this.currentMode_.getCommand();
+      if (command) {  // Could be null if the user did not do anything.
+        this.commandQueue_.execute(command);
+        this.updateUndoRedo();
+      }
+    }
+
+    var filesToggleRipple =
+        this.currentTool_.querySelector('files-toggle-ripple');
+    if (filesToggleRipple) {
+      filesToggleRipple.activated = false;
+    }
+
+    this.exitButton_.hidden = false;
+
+    this.currentMode_.cleanUpCaches();
+    this.currentMode_ = null;
+    this.currentTool_ = null;
+  }
+
+  /**
+   * Leave the mode, commit only if required by the current mode.
+   * @param {boolean} leaveToSwitchMode True if it leaves to switch mode.
+   */
+  leaveMode(leaveToSwitchMode) {
+    this.leaveModeInternal_(
+        !!this.currentMode_ && this.currentMode_.updated_ &&
+            this.currentMode_.implicitCommit,
+        leaveToSwitchMode);
+  }
+
+  /**
+   * Enter the editor mode with the given name.
+   *
+   * @param {string} name Mode name.
+   * @private
+   */
+  enterModeByName_(name) {
+    for (var i = 0; i !== this.modes_.length; i++) {
+      var mode = this.modes_[i];
+      if (mode.name === name) {
+        if (!mode.button_.hasAttribute('disabled')) {
+          this.enterMode(mode);
+        }
+        return;
+      }
+    }
+    console.error('Mode "' + name + '" not found.');
+  }
+
+  /**
+   * Key down handler.
+   * @param {!Event} event The keydown event.
+   * @return {boolean} True if handled.
+   */
+  onKeyDown(event) {
+    if (this.currentMode_ && this.currentMode_.isConsumingKeyEvents()) {
+      return false;
+    }
+
+    switch (util.getKeyModifiers(event) + event.key) {
+      case 'Escape':
+      case 'Enter':
+        if (this.getMode()) {
+          this.leaveModeInternal_(
+              event.key === 'Enter', false /* not to switch mode */);
+          return true;
+        }
+        break;
+
+      case 'Ctrl-z':  // Ctrl+Z
+        if (this.commandQueue_.canUndo()) {
+          this.undo();
+          return true;
+        }
+        break;
+
+      case 'Ctrl-y':  // Ctrl+Y
+        if (this.commandQueue_.canRedo()) {
+          this.redo();
+          return true;
+        }
+        break;
+
+      case 'a':
+        this.enterModeByName_('autofix');
+        return true;
+
+      case 'b':
+        this.enterModeByName_('exposure');
+        return true;
+
+      case 'c':
+        this.enterModeByName_('crop');
+        return true;
+
+      case 'l':
+        this.enterModeByName_('rotate_left');
+        return true;
+
+      case 'r':
+        this.enterModeByName_('rotate_right');
+        return true;
+    }
     return false;
   }
 
-  switch (util.getKeyModifiers(event) + event.key) {
-    case 'Escape':
-    case 'Enter':
-      if (this.getMode()) {
-        this.leaveModeInternal_(event.key === 'Enter',
-            false /* not to switch mode */);
-        return true;
+  /**
+   * Double tap handler.
+   * @param {number} x X coordinate of the event.
+   * @param {number} y Y coordinate of the event.
+   * @private
+   */
+  onDoubleTap_(x, y) {
+    if (this.getMode()) {
+      var action = this.buffer_.getDoubleTapAction(x, y);
+      if (action === ImageBuffer.DoubleTapAction.COMMIT) {
+        this.leaveModeInternal_(true, false /* not to switch mode */);
+      } else if (action === ImageBuffer.DoubleTapAction.CANCEL) {
+        this.leaveModeInternal_(false, false /* not to switch mode */);
       }
-      break;
-
-    case 'Ctrl-z':  // Ctrl+Z
-      if (this.commandQueue_.canUndo()) {
-        this.undo();
-        return true;
-      }
-      break;
-
-    case 'Ctrl-y':  // Ctrl+Y
-      if (this.commandQueue_.canRedo()) {
-        this.redo();
-        return true;
-      }
-      break;
-
-    case 'a':
-      this.enterModeByName_('autofix');
-      return true;
-
-    case 'b':
-      this.enterModeByName_('exposure');
-      return true;
-
-    case 'c':
-      this.enterModeByName_('crop');
-      return true;
-
-    case 'l':
-      this.enterModeByName_('rotate_left');
-      return true;
-
-    case 'r':
-      this.enterModeByName_('rotate_right');
-      return true;
-  }
-  return false;
-};
-
-/**
- * Double tap handler.
- * @param {number} x X coordinate of the event.
- * @param {number} y Y coordinate of the event.
- * @private
- */
-ImageEditor.prototype.onDoubleTap_ = function(x, y) {
-  if (this.getMode()) {
-    var action = this.buffer_.getDoubleTapAction(x, y);
-    if (action === ImageBuffer.DoubleTapAction.COMMIT) {
-      this.leaveModeInternal_(true, false /* not to switch mode */);
-    } else if (action === ImageBuffer.DoubleTapAction.CANCEL) {
-      this.leaveModeInternal_(false, false /* not to switch mode */);
     }
   }
-};
 
-/**
- * Called when the user starts editing image.
- */
-ImageEditor.prototype.onStartEditing = function() {
-  this.calculateModeApplicativity_();
-};
+  /**
+   * Called when the user starts editing image.
+   */
+  onStartEditing() {
+    this.calculateModeApplicativity_();
+  }
+}
 
 /**
  * A helper object for panning the ImageBuffer.
- *
- * @param {!HTMLElement} rootContainer The top-level container.
- * @param {!HTMLElement} container The container for mouse events.
- * @param {!ImageBuffer} buffer Image buffer.
- * @constructor
- * @struct
  */
-ImageEditor.MouseControl = function(rootContainer, container, buffer) {
-  this.rootContainer_ = rootContainer;
-  this.container_ = container;
-  this.buffer_ = buffer;
+ImageEditor.MouseControl = class {
+  /**
+   * @param {!HTMLElement} rootContainer The top-level container.
+   * @param {!HTMLElement} container The container for mouse events.
+   * @param {!ImageBuffer} buffer Image buffer.
+   */
+  constructor(rootContainer, container, buffer) {
+    this.rootContainer_ = rootContainer;
+    this.container_ = container;
+    this.buffer_ = buffer;
 
-  var handlers = {
-    'touchstart': this.onTouchStart,
-    'touchend': this.onTouchEnd,
-    'touchcancel': this.onTouchCancel,
-    'touchmove': this.onTouchMove,
-    'mousedown': this.onMouseDown,
-    'mouseup': this.onMouseUp
-  };
+    var handlers = {
+      'touchstart': this.onTouchStart,
+      'touchend': this.onTouchEnd,
+      'touchcancel': this.onTouchCancel,
+      'touchmove': this.onTouchMove,
+      'mousedown': this.onMouseDown,
+      'mouseup': this.onMouseUp
+    };
 
-  for (var eventName in handlers) {
-    container.addEventListener(
-        eventName, handlers[eventName].bind(this), false);
+    for (var eventName in handlers) {
+      container.addEventListener(
+          eventName, handlers[eventName].bind(this), false);
+    }
+
+    // Mouse move handler has to be attached to the window to receive events
+    // from outside of the window. See: http://crbug.com/155705
+    window.addEventListener('mousemove', this.onMouseMove.bind(this), false);
+
+    /**
+     * @type {?ImageBuffer.DragHandler}
+     * @private
+     */
+    this.dragHandler_ = null;
+
+    /**
+     * @type {boolean}
+     * @private
+     */
+    this.dragHappened_ = false;
+
+    /**
+     * @type {?{x: number, y: number, time:number}}
+     * @private
+     */
+    this.touchStartInfo_ = null;
+
+    /**
+     * @type {?{x: number, y: number, time:number}}
+     * @private
+     */
+    this.previousTouchStartInfo_ = null;
   }
 
-  // Mouse move handler has to be attached to the window to receive events
-  // from outside of the window. See: http://crbug.com/155705
-  window.addEventListener('mousemove', this.onMouseMove.bind(this), false);
-
   /**
-   * @type {?ImageBuffer.DragHandler}
+   * Returns an event's position.
+   *
+   * @param {!(MouseEvent|Touch)} e Pointer position.
+   * @return {!Object} A pair of x,y in page coordinates.
    * @private
    */
-  this.dragHandler_ = null;
+  static getPosition_(e) {
+    return {x: e.pageX, y: e.pageY};
+  }
 
   /**
-   * @type {boolean}
+   * Returns touch position or null if there is more than one touch position.
+   *
+   * @param {!TouchEvent} e Event.
+   * @return {Object?} A pair of x,y in page coordinates.
    * @private
    */
-  this.dragHappened_ = false;
+  getTouchPosition_(e) {
+    if (e.targetTouches.length == 1) {
+      return ImageEditor.MouseControl.getPosition_(e.targetTouches[0]);
+    } else {
+      return null;
+    }
+  }
 
   /**
-   * @type {?{x: number, y: number, time:number}}
-   * @private
+   * Touch start handler.
+   * @param {!TouchEvent} e Event.
    */
-  this.touchStartInfo_ = null;
+  onTouchStart(e) {
+    var position = this.getTouchPosition_(e);
+    if (position) {
+      this.touchStartInfo_ = {x: position.x, y: position.y, time: Date.now()};
+      this.dragHandler_ =
+          this.buffer_.getDragHandler(position.x, position.y, true /* touch */);
+      this.dragHappened_ = false;
+    }
+  }
 
   /**
-   * @type {?{x: number, y: number, time:number}}
+   * Touch end handler.
+   * @param {!TouchEvent} e Event.
+   */
+  onTouchEnd(e) {
+    if (!this.dragHappened_ && this.touchStartInfo_ &&
+        Date.now() - this.touchStartInfo_.time <=
+            ImageEditor.MouseControl.MAX_TAP_DURATION_) {
+      this.buffer_.onClick(this.touchStartInfo_.x, this.touchStartInfo_.y);
+      if (this.previousTouchStartInfo_ &&
+          Date.now() - this.previousTouchStartInfo_.time <
+              ImageEditor.MouseControl.MAX_DOUBLE_TAP_DURATION_) {
+        var prevTouchCircle = new Circle(
+            this.previousTouchStartInfo_.x, this.previousTouchStartInfo_.y,
+            ImageEditor.MouseControl.MAX_DISTANCE_FOR_DOUBLE_TAP_);
+        if (prevTouchCircle.inside(
+                this.touchStartInfo_.x, this.touchStartInfo_.y)) {
+          this.doubleTapCallback_(
+              this.touchStartInfo_.x, this.touchStartInfo_.y);
+        }
+      }
+      this.previousTouchStartInfo_ = this.touchStartInfo_;
+    } else {
+      this.previousTouchStartInfo_ = null;
+    }
+    this.onTouchCancel();
+  }
+
+  /**
+   * Default double tap handler.
+   * @param {number} x X coordinate of the event.
+   * @param {number} y Y coordinate of the event.
    * @private
    */
-  this.previousTouchStartInfo_ = null;
+  doubleTapCallback_(x, y) {}
+
+  /**
+   * Sets callback to be called when double tap detected.
+   * @param {function(number, number)} callback New double tap callback.
+   */
+  setDoubleTapCallback(callback) {
+    this.doubleTapCallback_ = callback;
+  }
+
+  /**
+   * Touch cancel handler.
+   */
+  onTouchCancel() {
+    this.dragHandler_ = null;
+    this.dragHappened_ = false;
+    this.touchStartInfo_ = null;
+    this.lockMouse_(false);
+  }
+
+  /**
+   * Touch move handler.
+   * @param {!TouchEvent} e Event.
+   */
+  onTouchMove(e) {
+    var position = this.getTouchPosition_(e);
+    if (!position) {
+      return;
+    }
+
+    if (this.touchStartInfo_ && !this.dragHappened_) {
+      var tapCircle = new Circle(
+          this.touchStartInfo_.x, this.touchStartInfo_.y,
+          ImageEditor.MouseControl.MAX_MOVEMENT_FOR_TAP_);
+      this.dragHappened_ = !tapCircle.inside(position.x, position.y);
+    }
+    if (this.dragHandler_ && this.dragHappened_) {
+      this.dragHandler_(position.x, position.y, e.shiftKey);
+      this.lockMouse_(true);
+    }
+  }
+
+  /**
+   * Mouse down handler.
+   * @param {!MouseEvent} e Event.
+   */
+  onMouseDown(e) {
+    var position = ImageEditor.MouseControl.getPosition_(e);
+
+    this.dragHandler_ =
+        this.buffer_.getDragHandler(position.x, position.y, false /* mouse */);
+    this.dragHappened_ = false;
+    this.updateCursor_(position);
+  }
+
+  /**
+   * Mouse up handler.
+   * @param {!MouseEvent} e Event.
+   */
+  onMouseUp(e) {
+    var position = ImageEditor.MouseControl.getPosition_(e);
+
+    if (!this.dragHappened_) {
+      this.buffer_.onClick(position.x, position.y);
+    }
+    this.dragHandler_ = null;
+    this.dragHappened_ = false;
+    this.lockMouse_(false);
+  }
+
+  /**
+   * Mouse move handler.
+   * @param {!Event} e Event.
+   */
+  onMouseMove(e) {
+    e = assertInstanceof(e, MouseEvent);
+    var position = ImageEditor.MouseControl.getPosition_(e);
+
+    if (this.dragHandler_ && !e.which) {
+      // mouseup must have happened while the mouse was outside our window.
+      this.dragHandler_ = null;
+      this.lockMouse_(false);
+    }
+
+    this.updateCursor_(position);
+    if (this.dragHandler_) {
+      this.dragHandler_(position.x, position.y, e.shiftKey);
+      this.dragHappened_ = true;
+      this.lockMouse_(true);
+    }
+  }
+
+  /**
+   * Update the UI to reflect mouse drag state.
+   * @param {boolean} on True if dragging.
+   * @private
+   */
+  lockMouse_(on) {
+    ImageUtil.setAttribute(this.rootContainer_, 'mousedrag', on);
+  }
+
+  /**
+   * Update the cursor.
+   *
+   * @param {!Object} position An object holding x and y properties.
+   * @private
+   */
+  updateCursor_(position) {
+    var oldCursor = this.container_.getAttribute('cursor');
+    var newCursor = this.buffer_.getCursorStyle(
+        position.x, position.y, !!this.dragHandler_);
+    if (newCursor != oldCursor) {  // Avoid flicker.
+      this.container_.setAttribute('cursor', newCursor);
+    }
+  }
 };
 
 /**
@@ -748,201 +940,3 @@
  * @const
  */
 ImageEditor.MouseControl.MAX_DOUBLE_TAP_DURATION_ = 1000;
-
-/**
- * Returns an event's position.
- *
- * @param {!(MouseEvent|Touch)} e Pointer position.
- * @return {!Object} A pair of x,y in page coordinates.
- * @private
- */
-ImageEditor.MouseControl.getPosition_ = function(e) {
-  return {
-    x: e.pageX,
-    y: e.pageY
-  };
-};
-
-/**
- * Returns touch position or null if there is more than one touch position.
- *
- * @param {!TouchEvent} e Event.
- * @return {Object?} A pair of x,y in page coordinates.
- * @private
- */
-ImageEditor.MouseControl.prototype.getTouchPosition_ = function(e) {
-  if (e.targetTouches.length == 1) {
-    return ImageEditor.MouseControl.getPosition_(e.targetTouches[0]);
-  } else {
-    return null;
-  }
-};
-
-/**
- * Touch start handler.
- * @param {!TouchEvent} e Event.
- */
-ImageEditor.MouseControl.prototype.onTouchStart = function(e) {
-  var position = this.getTouchPosition_(e);
-  if (position) {
-    this.touchStartInfo_ = {
-      x: position.x,
-      y: position.y,
-      time: Date.now()
-    };
-    this.dragHandler_ = this.buffer_.getDragHandler(position.x, position.y,
-                                                    true /* touch */);
-    this.dragHappened_ = false;
-  }
-};
-
-/**
- * Touch end handler.
- * @param {!TouchEvent} e Event.
- */
-ImageEditor.MouseControl.prototype.onTouchEnd = function(e) {
-  if (!this.dragHappened_ &&
-      this.touchStartInfo_ &&
-      Date.now() - this.touchStartInfo_.time <=
-          ImageEditor.MouseControl.MAX_TAP_DURATION_) {
-    this.buffer_.onClick(this.touchStartInfo_.x, this.touchStartInfo_.y);
-    if (this.previousTouchStartInfo_ &&
-        Date.now() - this.previousTouchStartInfo_.time <
-            ImageEditor.MouseControl.MAX_DOUBLE_TAP_DURATION_) {
-      var prevTouchCircle = new Circle(
-          this.previousTouchStartInfo_.x,
-          this.previousTouchStartInfo_.y,
-          ImageEditor.MouseControl.MAX_DISTANCE_FOR_DOUBLE_TAP_);
-      if (prevTouchCircle.inside(this.touchStartInfo_.x,
-                                 this.touchStartInfo_.y)) {
-        this.doubleTapCallback_(this.touchStartInfo_.x, this.touchStartInfo_.y);
-      }
-    }
-    this.previousTouchStartInfo_ = this.touchStartInfo_;
-  } else {
-    this.previousTouchStartInfo_ = null;
-  }
-  this.onTouchCancel();
-};
-
-/**
- * Default double tap handler.
- * @param {number} x X coordinate of the event.
- * @param {number} y Y coordinate of the event.
- * @private
- */
-ImageEditor.MouseControl.prototype.doubleTapCallback_ = function(x, y) {};
-
-/**
- * Sets callback to be called when double tap detected.
- * @param {function(number, number)} callback New double tap callback.
- */
-ImageEditor.MouseControl.prototype.setDoubleTapCallback = function(callback) {
-  this.doubleTapCallback_ = callback;
-};
-
-/**
- * Touch cancel handler.
- */
-ImageEditor.MouseControl.prototype.onTouchCancel = function() {
-  this.dragHandler_ = null;
-  this.dragHappened_ = false;
-  this.touchStartInfo_ = null;
-  this.lockMouse_(false);
-};
-
-/**
- * Touch move handler.
- * @param {!TouchEvent} e Event.
- */
-ImageEditor.MouseControl.prototype.onTouchMove = function(e) {
-  var position = this.getTouchPosition_(e);
-  if (!position) {
-    return;
-  }
-
-  if (this.touchStartInfo_ && !this.dragHappened_) {
-    var tapCircle = new Circle(
-        this.touchStartInfo_.x, this.touchStartInfo_.y,
-        ImageEditor.MouseControl.MAX_MOVEMENT_FOR_TAP_);
-    this.dragHappened_ = !tapCircle.inside(position.x, position.y);
-  }
-  if (this.dragHandler_ && this.dragHappened_) {
-    this.dragHandler_(position.x, position.y, e.shiftKey);
-    this.lockMouse_(true);
-  }
-};
-
-/**
- * Mouse down handler.
- * @param {!MouseEvent} e Event.
- */
-ImageEditor.MouseControl.prototype.onMouseDown = function(e) {
-  var position = ImageEditor.MouseControl.getPosition_(e);
-
-  this.dragHandler_ = this.buffer_.getDragHandler(position.x, position.y,
-                                                  false /* mouse */);
-  this.dragHappened_ = false;
-  this.updateCursor_(position);
-};
-
-/**
- * Mouse up handler.
- * @param {!MouseEvent} e Event.
- */
-ImageEditor.MouseControl.prototype.onMouseUp = function(e) {
-  var position = ImageEditor.MouseControl.getPosition_(e);
-
-  if (!this.dragHappened_) {
-    this.buffer_.onClick(position.x, position.y);
-  }
-  this.dragHandler_ = null;
-  this.dragHappened_ = false;
-  this.lockMouse_(false);
-};
-
-/**
- * Mouse move handler.
- * @param {!Event} e Event.
- */
-ImageEditor.MouseControl.prototype.onMouseMove = function(e) {
-  e = assertInstanceof(e, MouseEvent);
-  var position = ImageEditor.MouseControl.getPosition_(e);
-
-  if (this.dragHandler_ && !e.which) {
-    // mouseup must have happened while the mouse was outside our window.
-    this.dragHandler_ = null;
-    this.lockMouse_(false);
-  }
-
-  this.updateCursor_(position);
-  if (this.dragHandler_) {
-    this.dragHandler_(position.x, position.y, e.shiftKey);
-    this.dragHappened_ = true;
-    this.lockMouse_(true);
-  }
-};
-
-/**
- * Update the UI to reflect mouse drag state.
- * @param {boolean} on True if dragging.
- * @private
- */
-ImageEditor.MouseControl.prototype.lockMouse_ = function(on) {
-  ImageUtil.setAttribute(this.rootContainer_, 'mousedrag', on);
-};
-
-/**
- * Update the cursor.
- *
- * @param {!Object} position An object holding x and y properties.
- * @private
- */
-ImageEditor.MouseControl.prototype.updateCursor_ = function(position) {
-  var oldCursor = this.container_.getAttribute('cursor');
-  var newCursor = this.buffer_.getCursorStyle(
-      position.x, position.y, !!this.dragHandler_);
-  if (newCursor != oldCursor) {  // Avoid flicker.
-    this.container_.setAttribute('cursor', newCursor);
-  }
-};
diff --git a/ui/file_manager/image_loader/piex/images.golden.txt b/ui/file_manager/image_loader/piex/images.golden.txt
index 7dd68a5f..6330506 100644
--- a/ui/file_manager/image_loader/piex/images.golden.txt
+++ b/ui/file_manager/image_loader/piex/images.golden.txt
@@ -21,10 +21,10 @@
 test: images/FUJI_E550_RAW.RAF preview hash ada7914438 {"colorSpace":"sRgb","orientation":1,"format":0,"offset":148,"length":667499,"width":0,"height":0,"type":"preview"}
 test: images/FUJI_E550_RAW.RAF thumbnail hash 136fb97598 {"colorSpace":"sRgb","orientation":1,"format":0,"offset":1466,"length":9312,"width":0,"height":0,"type":"thumbnail"}
 test: images/NIKON_UB20_O35.NEF
-test: images/NIKON_UB20_O35.NEF details hash 5b4583c3 {"cameraMaker":"NIKON CORPORATION","cameraModel":"NIKON D1X","aperture":11,"focalLength":24,"exposureTime":0.006,"isoSpeed":0,"width":4028,"height":1324,"orientation":1,"colorSpace":"sRGB","date":"2004:07:30 12:23:24"}
+test: images/NIKON_UB20_O35.NEF details hash 731decb73 {"cameraMaker":"NIKON CORPORATION","cameraModel":"NIKON D1X","aperture":11,"focalLength":24,"exposureTime":0.006,"width":4028,"height":1324,"orientation":1,"colorSpace":"sRGB","date":"2004:07:30 12:23:24"}
 test: images/NIKON_UB20_O35.NEF thumbnail hash 71591f3c17 {"colorSpace":"sRgb","orientation":1,"format":1,"offset":32418,"length":57600,"width":160,"height":120,"size":57600,"type":"thumbnail"}
 test: images/NIKON_GDN0447.NEF
-test: images/NIKON_GDN0447.NEF details hash 30a7e5c1c {"cameraMaker":"NIKON CORPORATION","cameraModel":"NIKON D700","aperture":8,"focalLength":20,"exposureTime":0.5,"isoSpeed":0,"width":4256,"height":2832,"orientation":1,"colorSpace":"sRGB","date":"2018:01:17 12:31:01"}
+test: images/NIKON_GDN0447.NEF details hash 601825b88 {"cameraMaker":"NIKON CORPORATION","cameraModel":"NIKON D700","aperture":8,"focalLength":20,"exposureTime":0.5,"width":4256,"height":2832,"orientation":1,"colorSpace":"sRGB","date":"2018:01:17 12:31:01"}
 test: images/NIKON_GDN0447.NEF preview hash a49796a236 {"colorSpace":"sRgb","orientation":1,"format":0,"offset":175616,"length":1747382,"width":4256,"height":2832,"type":"preview"}
 test: images/NIKON_GDN0447.NEF thumbnail hash ec18fa833 {"colorSpace":"sRgb","orientation":1,"format":1,"offset":117638,"length":57600,"width":160,"height":120,"size":57600,"type":"thumbnail"}
 test: images/OLYMPUS_SC877.ORF
diff --git a/ui/file_manager/image_loader/piex/piex.cpp b/ui/file_manager/image_loader/piex/piex.cpp
index 08c4f25..5b62a90 100644
--- a/ui/file_manager/image_loader/piex/piex.cpp
+++ b/ui/file_manager/image_loader/piex/piex.cpp
@@ -81,7 +81,7 @@
     object.set("aperture", GetRational(image.fnumber));
     object.set("focalLength", GetRational(image.focal_length));
     object.set("exposureTime", GetRational(image.exposure_time));
-    object.set("isoSpeed", emscripten::val(image.iso));
+    object.set("isoSpeed", GetNonZeroValue(image.iso));
 
     object.set("width", emscripten::val(image.full_width));
     object.set("height", emscripten::val(image.full_height));
@@ -144,6 +144,10 @@
     return emscripten::val("sRgb");
   }
 
+  static emscripten::val GetNonZeroValue(const uint32_t value) {
+    return value > 0 ? emscripten::val(value) : emscripten::val::undefined();
+  }
+
   static float GetRational(const piex::PreviewImageData::Rational& number) {
     if (number.denominator != 0)
       return float(number.numerator) / number.denominator;
diff --git a/ui/file_manager/image_loader/piex/piex.out.wasm b/ui/file_manager/image_loader/piex/piex.out.wasm
index ff2cf2f..e19293c6 100644
--- a/ui/file_manager/image_loader/piex/piex.out.wasm
+++ b/ui/file_manager/image_loader/piex/piex.out.wasm
Binary files differ
diff --git a/ui/file_manager/integration_tests/file_manager/drive_specific.js b/ui/file_manager/integration_tests/file_manager/drive_specific.js
index 394baba..e849dccb4 100644
--- a/ui/file_manager/integration_tests/file_manager/drive_specific.js
+++ b/ui/file_manager/integration_tests/file_manager/drive_specific.js
@@ -143,6 +143,12 @@
 
   await remoteCall.waitForFiles(
       appId, TestEntryInfo.getExpectedRows(SEARCH_RESULTS_ENTRY_SET));
+
+  // Fetch A11y messages.
+  const a11yMessages =
+      await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []);
+  chrome.test.assertEq(1, a11yMessages.length, 'Missing a11y message');
+  chrome.test.assertEq('Showing results for hello.', a11yMessages[0]);
 };
 
 /**
@@ -158,6 +164,36 @@
       ['#search-box cr-input', 'Enter', false, false, false]));
   await remoteCall.waitForFiles(
       appId, TestEntryInfo.getExpectedRows(SEARCH_RESULTS_ENTRY_SET));
+
+  // Fetch A11y messages.
+  const a11yMessages =
+      await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []);
+  chrome.test.assertEq(1, a11yMessages.length, 'Missing a11y message');
+  chrome.test.assertEq('Showing results for hello.', a11yMessages[0]);
+
+  return appId;
+};
+
+/**
+ * Tests that pressing the clear search button announces an a11y message and
+ * shows all files/folders.
+ */
+testcase.drivePressClearSearch = async () => {
+  const appId = await testcase.drivePressEnterToSearch();
+
+  // Click on the clear search button.
+  await remoteCall.waitAndClickElement(appId, '#search-box cr-input .clear');
+
+  // Wait for fil list to display all files.
+  await remoteCall.waitForFiles(
+      appId, TestEntryInfo.getExpectedRows(BASIC_DRIVE_ENTRY_SET));
+
+  // Check that a11y message for clearing the search term has been issued.
+  const a11yMessages =
+      await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []);
+  chrome.test.assertEq(2, a11yMessages.length, 'Missing a11y message');
+  chrome.test.assertEq(
+      'Search text cleared, showing all files and folders.', a11yMessages[1]);
 };
 
 /**
diff --git a/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js b/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
index 486e58d8..ac9c0d30 100644
--- a/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
+++ b/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
@@ -108,24 +108,35 @@
    * @return {Promise} Promise fulfilled on success.
    */
   async function removeShortcut(appId, directory) {
-    // Focus the item first since actions are calculated asynchronously. The
-    // context menu wouldn't show if there are no visible items. Focusing first,
-    // will force the actions controller to refresh actions.
-    // TODO(mtomasz): Remove this hack (if possible).
-    chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
-        'focus', appId, [directory.navItem]));
+    const caller = getCaller();
+    const removeShortcutMenuItem =
+        '[command="#remove-folder-shortcut"]:not([hidden]):not([disabled])';
 
-    chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
-        'fakeMouseRightClick', appId, [directory.navItem]));
+    // Right-click for context menu with retry.
+    await repeatUntil(async () => {
+      // Right click.
+      chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+          'fakeMouseRightClick', appId, [directory.navItem]));
 
+      // Wait context menu to show.
+      await remoteCall.waitForElement(
+          appId, '#roots-context-menu:not([hidden])');
 
-    await remoteCall.waitForElement(appId, '#roots-context-menu:not([hidden])');
-    await remoteCall.waitForElement(
+      // Check menu item is visible and enabled.
+      const menuItem = await remoteCall.callRemoteTestUtil(
+          'queryAllElements', appId, [removeShortcutMenuItem]);
+      if (menuItem.length > 0) {
+        return true;
+      }
+
+      return pending(
+          caller,
+          `Waiting "remove shortcut" menu item to be available on ${appId}.`);
+    });
+
+    // Click the remove shortcut menu item.
+    await remoteCall.waitAndClickElement(
         appId,
-        '[command="#remove-folder-shortcut"]:not([hidden]):not([disabled])');
-
-    await remoteCall.callRemoteTestUtil(
-        'fakeMouseClick', appId,
         ['#roots-context-menu [command="#remove-folder-shortcut"]:' +
          'not([hidden])']);
 
diff --git a/ui/file_manager/integration_tests/file_manager/grid_view.js b/ui/file_manager/integration_tests/file_manager/grid_view.js
index b42eb68..33bd87a2 100644
--- a/ui/file_manager/integration_tests/file_manager/grid_view.js
+++ b/ui/file_manager/integration_tests/file_manager/grid_view.js
@@ -41,6 +41,7 @@
         caller, 'Failed to compare the grid lables, expected: %j, actual %j.',
         expectedLabels, actualLabels);
   });
+  return appId;
 }
 
 /**
@@ -56,3 +57,31 @@
 testcase.showGridViewDrive = () => {
   return showGridView(RootPath.DRIVE, BASIC_DRIVE_ENTRY_SET);
 };
+
+/**
+ * Tests to view-button switches to thumbnail (grid) view and clicking again
+ * switches back to detail (file list) view.
+ */
+testcase.showGridViewButtonSwitches = async () => {
+  const appId = await showGridView(RootPath.DOWNLOADS, BASIC_LOCAL_ENTRY_SET);
+
+  // Check that a11y message for switching to grid view.
+  let a11yMessages =
+      await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []);
+  chrome.test.assertEq(1, a11yMessages.length, 'Missing a11y message');
+  chrome.test.assertEq(
+      'File list has changed to thumbnail view.', a11yMessages[0]);
+
+  // Click view-button again to switch to detail view.
+  await remoteCall.waitAndClickElement(appId, '#view-button');
+
+  // Wait for detail-view to be visible and grid to be hidden.
+  await remoteCall.waitForElement(appId, '#detail-table:not([hidden])');
+  await remoteCall.waitForElement(appId, 'grid[hidden]');
+
+  // Check that a11y message for switching to list view.
+  a11yMessages =
+      await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []);
+  chrome.test.assertEq(2, a11yMessages.length, 'Missing a11y message');
+  chrome.test.assertEq('File list has changed to list view.', a11yMessages[1]);
+};
diff --git a/ui/file_manager/integration_tests/file_manager/search.js b/ui/file_manager/integration_tests/file_manager/search.js
new file mode 100644
index 0000000..cf6000ed
--- /dev/null
+++ b/ui/file_manager/integration_tests/file_manager/search.js
@@ -0,0 +1,103 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+(() => {
+  /**
+   * Expected files shown in the search results for 'hello'
+   *
+   * @type {Array<TestEntryInfo>}
+   * @const
+   */
+  const SEARCH_RESULTS_ENTRY_SET = [
+    ENTRIES.hello,
+  ];
+
+  /**
+   * Tests searching inside Downloads with results.
+   */
+  testcase.searchDownloadsWithResults = async () => {
+    // Open Files app on Downloads.
+    const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
+
+    // Click on the search button to display the search box.
+    await remoteCall.waitAndClickElement(appId, '#search-button');
+
+    // Focus the search box.
+    chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+        'fakeEvent', appId, ['#search-box cr-input', 'focus']));
+
+    // Input a text.
+    await remoteCall.callRemoteTestUtil(
+        'inputText', appId, ['#search-box cr-input', 'hello']);
+
+    // Notify the element of the input.
+    chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+        'fakeEvent', appId, ['#search-box cr-input', 'input']));
+
+    // Wait file list to display the search result.
+    await remoteCall.waitForFiles(
+        appId, TestEntryInfo.getExpectedRows(SEARCH_RESULTS_ENTRY_SET));
+
+    // Check that a11y message for results has been issued.
+    const a11yMessages =
+        await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []);
+    chrome.test.assertEq(1, a11yMessages.length, 'Missing a11y message');
+    chrome.test.assertEq('Showing results for hello.', a11yMessages[0]);
+
+    return appId;
+  };
+
+  /**
+   * Tests searching inside Downloads without results.
+   */
+  testcase.searchDownloadsWithNoResults = async () => {
+    // Open Files app on Downloads.
+    const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
+
+    // Focus the search box.
+    chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+        'fakeEvent', appId, ['#search-box cr-input', 'focus']));
+
+    // Input a text.
+    await remoteCall.callRemoteTestUtil(
+        'inputText', appId, ['#search-box cr-input', 'INVALID TERM']);
+
+    // Notify the element of the input.
+    chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+        'fakeEvent', appId, ['#search-box cr-input', 'input']));
+
+    // Wait file list to display no results.
+    await remoteCall.waitForFiles(appId, []);
+
+    // Check that a11y message for no results has been issued.
+    const a11yMessages =
+        await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []);
+    chrome.test.assertEq(1, a11yMessages.length, 'Missing a11y message');
+    chrome.test.assertEq(
+        'There are no results for INVALID TERM.', a11yMessages[0]);
+  };
+
+  /**
+   * Tests that clearing the search box announces the A11y.
+   */
+  testcase.searchDownloadsClearSearch = async () => {
+    // Perform a normal search, to be able to clear the search box.
+    const appId = await testcase.searchDownloadsWithResults();
+
+    // Click on the clear search button.
+    await remoteCall.waitAndClickElement(appId, '#search-box cr-input .clear');
+
+    // Wait for fil list to display all files.
+    await remoteCall.waitForFiles(
+        appId, TestEntryInfo.getExpectedRows(BASIC_LOCAL_ENTRY_SET));
+
+    // Check that a11y message for clearing the search term has been issued.
+    const a11yMessages =
+        await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []);
+    chrome.test.assertEq(2, a11yMessages.length, 'Missing a11y message');
+    chrome.test.assertEq(
+        'Search text cleared, showing all files and folders.', a11yMessages[1]);
+  };
+})();
diff --git a/ui/file_manager/integration_tests/file_manager_test_manifest.json b/ui/file_manager/integration_tests/file_manager_test_manifest.json
index 73b9c03..9d33742 100644
--- a/ui/file_manager/integration_tests/file_manager_test_manifest.json
+++ b/ui/file_manager/integration_tests/file_manager_test_manifest.json
@@ -42,6 +42,7 @@
       "file_manager/recents.js",
       "file_manager/restore_geometry.js",
       "file_manager/restore_prefs.js",
+      "file_manager/search.js",
       "file_manager/share_and_manage_dialog.js",
       "file_manager/sort_columns.js",
       "file_manager/suggest_app_dialog.js",
diff --git a/ui/message_center/views/notification_header_view.cc b/ui/message_center/views/notification_header_view.cc
index 998b5530..f13109d 100644
--- a/ui/message_center/views/notification_header_view.cc
+++ b/ui/message_center/views/notification_header_view.cc
@@ -19,12 +19,10 @@
 #include "ui/message_center/vector_icons.h"
 #include "ui/message_center/views/relative_time_formatter.h"
 #include "ui/strings/grit/ui_strings.h"
-#include "ui/views/animation/ink_drop_stub.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
-#include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_types.h"
 #include "ui/views/painter.h"
@@ -199,27 +197,35 @@
   AddChildView(app_name_view_);
   layout->SetFlexForView(app_name_view_, kAppNameFlex);
 
+  // Detail views which will be hidden in settings mode.
+  detail_views_ = new views::View();
+  auto* detail_layout =
+      detail_views_->SetLayoutManager(std::make_unique<views::FlexLayout>());
+  detail_layout->SetCollapseMargins(true);
+  detail_layout->SetDefaultChildMargins(kHeaderSpacing);
+  AddChildView(detail_views_);
+
   // Summary text divider
   summary_text_divider_ = create_label();
   summary_text_divider_->SetText(base::WideToUTF16(kNotificationHeaderDivider));
   summary_text_divider_->SetVisible(false);
-  AddChildView(summary_text_divider_);
+  detail_views_->AddChildView(summary_text_divider_);
 
   // Summary text view
   summary_text_view_ = create_label();
   summary_text_view_->SetVisible(false);
-  AddChildView(summary_text_view_);
+  detail_views_->AddChildView(summary_text_view_);
 
   // Timestamp divider
   timestamp_divider_ = create_label();
   timestamp_divider_->SetText(base::WideToUTF16(kNotificationHeaderDivider));
   timestamp_divider_->SetVisible(false);
-  AddChildView(timestamp_divider_);
+  detail_views_->AddChildView(timestamp_divider_);
 
   // Timestamp view
   timestamp_view_ = create_label();
   timestamp_view_->SetVisible(false);
-  AddChildView(timestamp_view_);
+  detail_views_->AddChildView(timestamp_view_);
 
   // Expand button view
   expand_button_ = new ExpandButton();
@@ -228,7 +234,7 @@
   expand_button_->SetHorizontalAlignment(views::ImageView::Alignment::kLeading);
   expand_button_->SetImageSize(gfx::Size(kExpandIconSize, kExpandIconSize));
   DCHECK_EQ(kInnerHeaderHeight, expand_button_->GetPreferredSize().height());
-  AddChildView(expand_button_);
+  detail_views_->AddChildView(expand_button_);
 
   // Spacer between left-aligned views and right-aligned views
   views::View* spacer = new views::View;
@@ -321,8 +327,8 @@
                      base::Unretained(this), timestamp));
 }
 
-void NotificationHeaderView::SetTimestampVisible(bool visible) {
-  timestamp_visible_ = visible;
+void NotificationHeaderView::SetDetailViewsVisible(bool visible) {
+  detail_views_->SetVisible(visible);
 
   if (visible && timestamp_)
     SetTimestamp(timestamp_.value());
@@ -333,11 +339,6 @@
 }
 
 void NotificationHeaderView::SetExpandButtonEnabled(bool enabled) {
-  // SetInkDropMode iff. the visibility changed.
-  // Otherwise, the ink drop animation cannot finish.
-  if (expand_button_->GetVisible() != enabled)
-    SetInkDropMode(enabled ? InkDropMode::ON : InkDropMode::OFF);
-
   expand_button_->SetVisible(enabled);
 }
 
@@ -373,10 +374,6 @@
   timestamp_view_->SetBackgroundColor(color);
 }
 
-bool NotificationHeaderView::IsExpandButtonEnabled() {
-  return expand_button_->GetVisible();
-}
-
 void NotificationHeaderView::SetSubpixelRenderingEnabled(bool enabled) {
   app_name_view_->SetSubpixelRenderingEnabled(enabled);
   summary_text_divider_->SetSubpixelRenderingEnabled(enabled);
@@ -385,10 +382,6 @@
   timestamp_view_->SetSubpixelRenderingEnabled(enabled);
 }
 
-std::unique_ptr<views::InkDrop> NotificationHeaderView::CreateInkDrop() {
-  return std::make_unique<views::InkDropStub>();
-}
-
 const base::string16& NotificationHeaderView::app_name_for_testing() const {
   return app_name_view_->text();
 }
@@ -406,12 +399,9 @@
   summary_text_divider_->SetVisible(summary_visible);
   summary_text_view_->SetVisible(summary_visible);
 
-  const bool timestamp_visible =
-      !has_progress_ && timestamp_visible_ && timestamp_;
+  const bool timestamp_visible = !has_progress_ && timestamp_;
   timestamp_divider_->SetVisible(timestamp_visible);
   timestamp_view_->SetVisible(timestamp_visible);
-
-  Layout();
 }
 
 }  // namespace message_center
diff --git a/ui/message_center/views/notification_header_view.h b/ui/message_center/views/notification_header_view.h
index e67234d3..c6b2ed1 100644
--- a/ui/message_center/views/notification_header_view.h
+++ b/ui/message_center/views/notification_header_view.h
@@ -28,6 +28,9 @@
   void SetAppName(const base::string16& name);
   void SetAppNameElideBehavior(gfx::ElideBehavior elide_behavior);
 
+  // Only show AppIcon and AppName in settings mode.
+  void SetDetailViewsVisible(bool visible);
+
   // Progress, summary and overflow indicator are all the same UI element so are
   // mutually exclusive.
   void SetProgress(int progress);
@@ -35,12 +38,8 @@
   void SetOverflowIndicator(int count);
 
   void SetTimestamp(base::Time timestamp);
-  void SetTimestampVisible(bool visible);
   void SetExpandButtonEnabled(bool enabled);
   void SetExpanded(bool expanded);
-  void SetSettingsButtonEnabled(bool enabled);
-  void SetCloseButtonEnabled(bool enabled);
-  void SetControlButtonsVisible(bool visible);
 
   // Set the unified theme color used among the app icon, app name, and expand
   // button.
@@ -52,15 +51,11 @@
 
   void ClearAppIcon();
   void ClearProgress();
-  bool IsExpandButtonEnabled();
   void SetSubpixelRenderingEnabled(bool enabled);
 
   // views::View:
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
-  // Button override:
-  std::unique_ptr<views::InkDrop> CreateInkDrop() override;
-
   views::ImageView* expand_button() { return expand_button_; }
 
   SkColor accent_color_for_testing() { return accent_color_; }
@@ -76,6 +71,8 @@
   const base::string16& timestamp_for_testing() const;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(NotificationHeaderViewTest, SettingsMode);
+
   // Update visibility for both |summary_text_view_| and |timestamp_view_|.
   void UpdateSummaryTextVisibility();
 
@@ -85,17 +82,16 @@
   base::OneShotTimer timestamp_update_timer_;
   base::Optional<base::Time> timestamp_;
 
+  views::ImageView* app_icon_view_ = nullptr;
   views::Label* app_name_view_ = nullptr;
+  views::View* detail_views_ = nullptr;
   views::Label* summary_text_divider_ = nullptr;
   views::Label* summary_text_view_ = nullptr;
   views::Label* timestamp_divider_ = nullptr;
   views::Label* timestamp_view_ = nullptr;
-  views::ImageView* app_icon_view_ = nullptr;
   views::ImageView* expand_button_ = nullptr;
 
-  bool settings_button_enabled_ = false;
   bool has_progress_ = false;
-  bool timestamp_visible_ = true;
   bool is_expanded_ = false;
   bool using_default_app_icon_ = false;
 
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc
index e5d6498..baedaa98 100644
--- a/ui/message_center/views/notification_view_md.cc
+++ b/ui/message_center/views/notification_view_md.cc
@@ -1255,7 +1255,7 @@
 
   settings_row_->SetVisible(inline_settings_visible);
   content_row_->SetVisible(!inline_settings_visible);
-  header_row_->SetTimestampVisible(!inline_settings_visible);
+  header_row_->SetDetailViewsVisible(!inline_settings_visible);
 
   // Always check "Don't block" when inline settings is shown.
   // If it's already blocked, users should not see inline settings.