diff --git a/DEPS b/DEPS
index 0e7c772..ab3fa923 100644
--- a/DEPS
+++ b/DEPS
@@ -174,7 +174,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'f703443b1f675f9205c8507e0bcf9d04e04c432e',
+  'angle_revision': '442ff2ebbd669ea22cbbf4b0daf24df3b503a12a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -225,7 +225,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': 'e00ffeb79abcfd6cf7219358f556111b4e0f56a3',
+  'catapult_revision': 'eecf29fbbcd30558fd54230ad2f2653c21bf7a25',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -281,7 +281,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '527a689307a4c15a0a910007327783a1435ac101',
+  'spv_tools_revision': '510ca9d616f5682a225b894a64b2af8fa5997d48',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -878,7 +878,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9a5bb612e62427fce53af10d2b72206f72d0ca40',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '07cec8f6cb597c025d596d96fe713ea28a47eb85',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1123,7 +1123,7 @@
   },
 
   'src/third_party/libjpeg_turbo':
-    Var('chromium_git') + '/chromium/deps/libjpeg_turbo.git' + '@' + 'baa5dc24258bf9af873b9105e8988c558c425a17',
+    Var('chromium_git') + '/chromium/deps/libjpeg_turbo.git' + '@' + '38c693569427129fd8c22f4bba67685d7e176094',
 
   'src/third_party/liblouis/src': {
       'url': Var('chromium_git') + '/external/liblouis-github.git' + '@' + '67ab8e96cc2f4db70f220f71cbdee0903e8abac6',
@@ -1257,7 +1257,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '692a818a2c7995ce9b6aa3e301758e92bbc92b35',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '380b0d5df68b65ec6b29cc19f51e2249d7f4febc',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1487,7 +1487,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d64438c0787f675972c256f77a8b410b162268c5',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6d92ddf0280ac8697411a12866e4d1c94c368fb8',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/multi_device_setup/multi_device_notification_presenter.cc b/ash/multi_device_setup/multi_device_notification_presenter.cc
index b0ec241..c28af15 100644
--- a/ash/multi_device_setup/multi_device_notification_presenter.cc
+++ b/ash/multi_device_setup/multi_device_notification_presenter.cc
@@ -76,7 +76,7 @@
 MultiDeviceNotificationPresenter::MultiDeviceNotificationPresenter(
     message_center::MessageCenter* message_center,
     service_manager::Connector* connector)
-    : message_center_(message_center), connector_(connector), binding_(this) {
+    : message_center_(message_center), connector_(connector) {
   DCHECK(message_center_);
   DCHECK(connector_);
 
@@ -227,11 +227,8 @@
       &multidevice_setup_ptr_);
 
   // Add this object as the delegate of the MultiDeviceSetup Service.
-  chromeos::multidevice_setup::mojom::AccountStatusChangeDelegatePtr
-      delegate_ptr;
-  binding_.Bind(mojo::MakeRequest(&delegate_ptr));
   multidevice_setup_ptr_->SetAccountStatusChangeDelegate(
-      std::move(delegate_ptr));
+      receiver_.BindNewPipeAndPassRemote());
 
   message_center_->AddObserver(this);
 }
diff --git a/ash/multi_device_setup/multi_device_notification_presenter.h b/ash/multi_device_setup/multi_device_notification_presenter.h
index 7aeab62..68490c9 100644
--- a/ash/multi_device_setup/multi_device_notification_presenter.h
+++ b/ash/multi_device_setup/multi_device_notification_presenter.h
@@ -14,7 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "ui/message_center/message_center_observer.h"
 
 namespace message_center {
@@ -128,8 +128,9 @@
 
   chromeos::multidevice_setup::mojom::MultiDeviceSetupPtr
       multidevice_setup_ptr_;
-  mojo::Binding<chromeos::multidevice_setup::mojom::AccountStatusChangeDelegate>
-      binding_;
+  mojo::Receiver<
+      chromeos::multidevice_setup::mojom::AccountStatusChangeDelegate>
+      receiver_{this};
 
   base::WeakPtrFactory<MultiDeviceNotificationPresenter> weak_ptr_factory_{
       this};
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardViewTest.java
index 04fabb8..5cdbb24 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardViewTest.java
@@ -125,42 +125,72 @@
     // Covers: IS_DENSE=false, MAX_ROWS=2, MAX_COLUMNS=4, numSites=MAX_COLUMNS, numBlacklisted=0
     @Test
     @SmallTest
-    public void testTileQuantityOriginalPerfectRow() throws Exception {
+    @CommandLineFlags.Add({"enable-features=ExploreSites<FakeStudyName",
+            "force-fieldtrials=FakeStudyName/Enabled",
+            "force-fieldtrial-params=FakeStudyName.Enabled:variation/mostLikelyTile/"
+                    + "denseVariation/original"})
+    public void
+    testTileQuantityOriginalPerfectRow() throws Exception {
         runTileQuantityTest(4, 0, false, 1, 4);
     }
 
     // Covers: IS_DENSE=false, MAX_ROWS=2, MAX_COLUMNS=4, numSites>MAX_COLUMNS, numBlacklisted=0
     @Test
     @SmallTest
-    public void testTileQuantityOriginalImperfectRow() throws Exception {
+    @CommandLineFlags.Add({"enable-features=ExploreSites<FakeStudyName",
+            "force-fieldtrials=FakeStudyName/Enabled",
+            "force-fieldtrial-params=FakeStudyName.Enabled:variation/mostLikelyTile/"
+                    + "denseVariation/original"})
+    public void
+    testTileQuantityOriginalImperfectRow() throws Exception {
         runTileQuantityTest(5, 0, false, 1, 4);
     }
 
     // Covers: IS_DENSE=false, MAX_ROWS=2, MAX_COLUMNS=4, numSites=MAX_COLUMNS, numBlacklisted>0
     @Test
     @SmallTest
-    public void testTileQuantityOriginalPerfectRowAfterBlacklisted() throws Exception {
+    @CommandLineFlags.Add({"enable-features=ExploreSites<FakeStudyName",
+            "force-fieldtrials=FakeStudyName/Enabled",
+            "force-fieldtrial-params=FakeStudyName.Enabled:variation/mostLikelyTile/"
+                    + "denseVariation/original"})
+    public void
+    testTileQuantityOriginalPerfectRowAfterBlacklisted() throws Exception {
         runTileQuantityTest(5, 1, false, 1, 4);
     }
 
     // Covers: IS_DENSE=false, MAX_ROWS=2, MAX_COLUMNS=4, numSites>MAX_COLUMNS, numBlacklisted>0
     @Test
     @SmallTest
-    public void testTileQuantityOriginalImperfectRowAfterBlacklisted() throws Exception {
+    @CommandLineFlags.Add({"enable-features=ExploreSites<FakeStudyName",
+            "force-fieldtrials=FakeStudyName/Enabled",
+            "force-fieldtrial-params=FakeStudyName.Enabled:variation/mostLikelyTile/"
+                    + "denseVariation/original"})
+    public void
+    testTileQuantityOriginalImperfectRowAfterBlacklisted() throws Exception {
         runTileQuantityTest(8, 2, false, 2, 6);
     }
 
     // Covers: IS_DENSE=true, MAX_ROWS=2, MAX_COLUMNS=4, numSites<MAX_COLUMNS, numBlacklisted>0
     @Test
     @SmallTest
-    public void testTileQuantityOriginalTooFewTilesAfterBlacklisted() throws Exception {
+    @CommandLineFlags.Add({"enable-features=ExploreSites<FakeStudyName",
+            "force-fieldtrials=FakeStudyName/Enabled",
+            "force-fieldtrial-params=FakeStudyName.Enabled:variation/mostLikelyTile/"
+                    + "denseVariation/original"})
+    public void
+    testTileQuantityOriginalTooFewTilesAfterBlacklisted() throws Exception {
         runTileQuantityTest(5, 4, false, 1, 1);
     }
 
     // Covers: IS_DENSE=true, MAX_ROWS=2, MAX_COLUMNS=4, numSites>MAX_TILE_COUNT, numBlacklisted=0
     @Test
     @SmallTest
-    public void testTileQuantityOriginalTooManyTiles() throws Exception {
+    @CommandLineFlags.Add({"enable-features=ExploreSites<FakeStudyName",
+            "force-fieldtrials=FakeStudyName/Enabled",
+            "force-fieldtrial-params=FakeStudyName.Enabled:variation/mostLikelyTile/"
+                    + "denseVariation/original"})
+    public void
+    testTileQuantityOriginalTooManyTiles() throws Exception {
         runTileQuantityTest(15, 0, false, 2, 8);
     }
 }
diff --git a/chrome/app/chrome_content_browser_overlay_manifest.cc b/chrome/app/chrome_content_browser_overlay_manifest.cc
index 033c814..be36cf1 100644
--- a/chrome/app/chrome_content_browser_overlay_manifest.cc
+++ b/chrome/app/chrome_content_browser_overlay_manifest.cc
@@ -83,10 +83,6 @@
 #include "third_party/blink/public/mojom/media_controls/touchless/media_controls.mojom.h"
 #endif
 
-#if defined(ENABLE_SPATIAL_NAVIGATION_HOST)
-#include "third_party/blink/public/mojom/page/spatial_navigation.mojom.h"
-#endif
-
 const service_manager::Manifest& GetChromeContentBrowserOverlayManifest() {
   static base::NoDestructor<service_manager::Manifest> manifest {
     service_manager::ManifestBuilder()
@@ -166,9 +162,6 @@
 #if defined(BROWSER_MEDIA_CONTROLS_MENU)
                 blink::mojom::MediaControlsMenuHost,
 #endif
-#if defined(ENABLE_SPATIAL_NAVIGATION_HOST)
-                blink::mojom::SpatialNavigationHost,
-#endif
                 blink::mojom::TextSuggestionHost,
                 chrome::mojom::OfflinePageAutoFetcher,
                 chrome::mojom::PrerenderCanceler,
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3dda0f3..976b4e05 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6575,16 +6575,16 @@
       <!-- Sharing features. -->
       <if expr="not use_titlecase">
         <message name="IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES" desc="The label of item for click to call in context menu when multiple devices are available.">
-          Call from your devices
+          Call from your device
         </message>
         <message name="IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE" desc="The label of item for click to call in context menu for showing device name when one device is available.">
           Call from <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
         </message>
         <message name="IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES" desc="The label of item for shared clipboard in context menu when multiple devices are available.">
-          Send text to your devices
+          Copy to your device
         </message>
         <message name="IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE" desc="The label of item for shared clipboard in context menu for showing device name when one device is available.">
-          Send text to <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
+          Copy to <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
         </message>
         <message name="IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_NOTIFICATION_TITLE" desc="The title of the notification shown when a clipboard was shared.">
           Text shared from <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
@@ -6596,16 +6596,16 @@
 
       <if expr="use_titlecase">
         <message name="IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES" desc="In Title Case: The label of item for click to call in context menu when multiple devices are available.">
-          Call from Your Devices
+          Call from Your Device
         </message>
         <message name="IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE" desc="In Title Case: The label of item for click to call in context menu when single device is available.">
           Call from <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
         </message>
         <message name="IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES" desc="In Title Case: The label of item for shared clipboard in context menu when multiple devices are available.">
-          Send text to Your Devices
+          Copy to Your Device
         </message>
         <message name="IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE" desc="In Title Case: The label of item for shared clipboard in context menu when single device is available.">
-          Send text to <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
+          Copy to <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
         </message>
         <message name="IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_NOTIFICATION_TITLE" desc="In Title Case: The title of the notification shown when a clipboard was shared.">
           Text shared from <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
diff --git a/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES.png.sha1 b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES.png.sha1
index 7db1032..63b9a2f 100644
--- a/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES.png.sha1
@@ -1 +1 @@
-d3ea7c88529dd93e5f403fa57d5df3bfadf8936a
\ No newline at end of file
+9d72c058ec472b608da7d8b2483890fc8c3cbec6
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES.png.sha1 b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES.png.sha1
index 041ca56..c2ed224 100644
--- a/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES.png.sha1
@@ -1 +1 @@
-d5d655b32a46fca7495984a4b84f6d8334686235
\ No newline at end of file
+f3c6957798e8bafde2f7f0fedf4df01cd46187fb
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE.png.sha1 b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE.png.sha1
index 7bb7d252..e4801da 100644
--- a/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE.png.sha1
@@ -1 +1 @@
-4e7f8e0291fe4dc8314a309da120e172d8937e4c
\ No newline at end of file
+4edc74d4e5f5d8fbf0ef8e98eb7934aff9092f39
\ No newline at end of file
diff --git a/chrome/browser/badging/badge_manager.cc b/chrome/browser/badging/badge_manager.cc
index 221fab6..be2da584 100644
--- a/chrome/browser/badging/badge_manager.cc
+++ b/chrome/browser/badging/badge_manager.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
@@ -98,7 +99,7 @@
   delegate_->OnBadgeUpdated(scope);
 }
 
-void BadgeManager::SetBadge(const GURL& scope,
+void BadgeManager::SetBadge(const GURL& /*scope*/,
                             blink::mojom::BadgeValuePtr mojo_value) {
   if (mojo_value->is_number() && mojo_value->get_number() == 0) {
     mojo::ReportBadMessage(
@@ -107,21 +108,25 @@
     return;
   }
 
-  if (!ScopeIsValidBadgeTarget(receivers_.current_context(), scope))
+  const base::Optional<GURL> app_scope =
+      GetAppScopeForContext(receivers_.current_context());
+  if (!app_scope)
     return;
 
   // Convert the mojo badge representation into a BadgeManager::BadgeValue.
   BadgeValue value = mojo_value->is_flag()
                          ? base::nullopt
                          : base::make_optional(mojo_value->get_number());
-  UpdateBadge(scope, base::make_optional(value));
+  UpdateBadge(app_scope.value(), base::make_optional(value));
 }
 
-void BadgeManager::ClearBadge(const GURL& scope) {
-  if (!ScopeIsValidBadgeTarget(receivers_.current_context(), scope))
+void BadgeManager::ClearBadge(const GURL& /*scope*/) {
+  const base::Optional<GURL> app_scope =
+      GetAppScopeForContext(receivers_.current_context());
+  if (!app_scope)
     return;
 
-  UpdateBadge(scope, base::nullopt);
+  UpdateBadge(app_scope.value(), base::nullopt);
 }
 
 GURL BadgeManager::MostSpecificBadgeForScope(const GURL& scope) {
@@ -144,14 +149,29 @@
   return best_match;
 }
 
-bool BadgeManager::ScopeIsValidBadgeTarget(const BindingContext& context,
-                                           const GURL& scope) {
+base::Optional<GURL> BadgeManager::GetAppScopeForContext(
+    const BindingContext& context) {
   content::RenderFrameHost* frame =
       content::RenderFrameHost::FromID(context.process_id, context.frame_id);
   if (!frame)
-    return false;
+    return base::nullopt;
 
-  return url::IsSameOriginWith(frame->GetLastCommittedURL(), scope);
+  content::WebContents* contents =
+      content::WebContents::FromRenderFrameHost(frame);
+  if (!contents)
+    return base::nullopt;
+
+  const web_app::AppRegistrar& registrar =
+      web_app::WebAppProviderBase::GetProviderBase(
+          Profile::FromBrowserContext(contents->GetBrowserContext()))
+          ->registrar();
+
+  const base::Optional<web_app::AppId> app_id =
+      registrar.FindAppWithUrlInScope(frame->GetLastCommittedURL());
+  if (!app_id)
+    return base::nullopt;
+
+  return registrar.GetAppScope(app_id.value());
 }
 
 std::string GetBadgeString(base::Optional<uint64_t> badge_content) {
diff --git a/chrome/browser/badging/badge_manager.h b/chrome/browser/badging/badge_manager.h
index a8bbb28..cd04096 100644
--- a/chrome/browser/badging/badge_manager.h
+++ b/chrome/browser/badging/badge_manager.h
@@ -75,16 +75,19 @@
   // blink::mojom::BadgeService:
   // Note: These are private to stop them being called outside of mojo as they
   // require a mojo binding context.
-  void SetBadge(const GURL& scope, blink::mojom::BadgeValuePtr value) override;
-  void ClearBadge(const GURL& scope) override;
+  // TODO(crbug.com/1006665): Remove scope from the mojo interface in SetBadge
+  // and ClearBadge.
+  void SetBadge(const GURL& /*scope*/,
+                blink::mojom::BadgeValuePtr value) override;
+  void ClearBadge(const GURL& /*scope*/) override;
 
   // Finds the scope URL of the most specific badge for |scope|. Returns
   // GURL::EmptyGURL() if no match is found.
   GURL MostSpecificBadgeForScope(const GURL& scope);
 
-  // Determines whether |context| is allowed to change the badge for |scope|.
-  bool ScopeIsValidBadgeTarget(const BindingContext& context,
-                               const GURL& scope);
+  // Finds the most specific app scope containing |context|. base::nullopt if
+  // no app contains |context|.
+  base::Optional<GURL> GetAppScopeForContext(const BindingContext& context);
 
   // All the mojo receivers for the BadgeManager. Keeps track of the
   // render_frame the binding is associated with, so as to not have to rely
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 5e03ec6..9369e04 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -17,6 +17,9 @@
 #include "content/public/browser/web_contents.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/mojom/webshare/webshare.mojom.h"
+#if defined(ENABLE_SPATIAL_NAVIGATION_HOST)
+#include "third_party/blink/public/mojom/page/spatial_navigation.mojom.h"
+#endif
 #else
 #include "chrome/browser/badging/badge_manager.h"
 #endif
@@ -52,6 +55,10 @@
 #if defined(OS_ANDROID)
   map->Add<blink::mojom::ShareService>(base::BindRepeating(
       &ForwardToJavaWebContents<blink::mojom::ShareService>));
+#if defined(ENABLE_SPATIAL_NAVIGATION_HOST)
+  map->Add<blink::mojom::SpatialNavigationHost>(base::BindRepeating(
+      &ForwardToJavaWebContents<blink::mojom::SpatialNavigationHost>));
+#endif
 #else
   map->Add<blink::mojom::BadgeService>(
       base::BindRepeating(&badging::BadgeManager::BindReceiver));
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 7c212a1..61ca82a 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -614,10 +614,6 @@
 #include "third_party/blink/public/mojom/media_controls/touchless/media_controls.mojom.h"
 #endif
 
-#if defined(ENABLE_SPATIAL_NAVIGATION_HOST)
-#include "third_party/blink/public/mojom/page/spatial_navigation.mojom.h"
-#endif
-
 using base::FileDescriptor;
 using content::BrowserThread;
 using content::BrowserURLHandler;
@@ -2005,16 +2001,6 @@
                                 content::RenderFrameHost* render_frame_host) {
   render_frame_host->GetJavaInterfaces()->GetInterface(std::move(request));
 }
-
-template <typename Interface>
-void ForwardToJavaWebContentsRegistry(
-    mojo::InterfaceRequest<Interface> request,
-    content::RenderFrameHost* render_frame_host) {
-  content::WebContents* contents =
-      content::WebContents::FromRenderFrameHost(render_frame_host);
-  if (contents)
-    contents->GetJavaInterfaces()->GetInterface(std::move(request));
-}
 #endif
 
 }  // namespace
@@ -4455,13 +4441,6 @@
   }
 #endif
 
-#if defined(OS_ANDROID)
-#if defined(ENABLE_SPATIAL_NAVIGATION_HOST)
-  frame_interfaces_parameterized_->AddInterface(base::Bind(
-      &ForwardToJavaWebContentsRegistry<blink::mojom::SpatialNavigationHost>));
-#endif
-#endif
-
   frame_interfaces_parameterized_->AddInterface(
       base::BindRepeating(&NavigationPredictor::Create));
 
diff --git a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
index 5e73bcd..9575930 100644
--- a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
+++ b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
@@ -153,7 +153,7 @@
 };
 
 // Tests a simple lock override.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, LockOverride) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DISABLED_LockOverride) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 GMT");
   ScreenLockerTester().Lock();
 
@@ -182,7 +182,7 @@
 }
 
 // Tests an unlock override on a bedtime.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, UnlockBedtime) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DISABLED_UnlockBedtime) {
   LogInChildAndSetupClockWithTime("5 Jan 2018 22:00:00 BRT");
   ScreenLockerTester().Lock();
 
@@ -234,7 +234,8 @@
 }
 
 // Tests an override with duration on a bedtime before it's locked.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, OverrideBedtimeWithDuration) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
+                       DISABLED_OverrideBedtimeWithDuration) {
   LogInChildAndSetupClockWithTime("5 Jan 2018 20:45:00 PST");
   ScreenLockerTester().Lock();
 
@@ -304,7 +305,7 @@
 
 // Tests an override with duration on a daily limit before it's locked.
 IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
-                       OverrideDailyLimitWithDuration) {
+                       DISABLED_OverrideDailyLimitWithDuration) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 BRT");
   ScreenLockerTester().Lock();
 
@@ -368,7 +369,8 @@
 }
 
 // Tests an unlock override with duration on a bedtime.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, UnlockBedtimeWithDuration) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
+                       DISABLED_UnlockBedtimeWithDuration) {
   LogInChildAndSetupClockWithTime("5 Jan 2018 22:00:00 GMT");
   ScreenLockerTester().Lock();
 
@@ -434,7 +436,8 @@
 }
 
 // Tests an unlock override with duration on a daily limit.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, UnlockDailyLimitWithDuration) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
+                       DISABLED_UnlockDailyLimitWithDuration) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 PST");
   ScreenLockerTester().Lock();
 
@@ -498,7 +501,7 @@
 }
 
 // Tests the default time window limit.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DefaultBedtime) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DISABLED_DefaultBedtime) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 GMT");
   ScreenLockerTester().Lock();
 
@@ -561,7 +564,7 @@
 }
 
 // Tests the default time window limit.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DefaultDailyLimit) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DISABLED_DefaultDailyLimit) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 GMT");
   ScreenLockerTester().Lock();
 
@@ -627,7 +630,8 @@
 }
 
 // Tests that the bedtime locks an active session when it is reached.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, ActiveSessionBedtime) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
+                       DISABLED_ActiveSessionBedtime) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 PST");
 
   system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
@@ -665,7 +669,8 @@
 }
 
 // Tests that the daily limit locks the device when it is reached.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, ActiveSessionDailyLimit) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
+                       DISABLED_ActiveSessionDailyLimit) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 PST");
 
   system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
@@ -701,7 +706,8 @@
 }
 
 // Tests bedtime during timezone changes.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, BedtimeOnTimezoneChange) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
+                       DISABLED_BedtimeOnTimezoneChange) {
   LogInChildAndSetupClockWithTime("3 Jan 2018 10:00:00 GMT-0600");
   ScreenLockerTester().Lock();
 
@@ -753,7 +759,7 @@
 
 // Tests bedtime during timezone changes that make the clock go back in time.
 IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
-                       BedtimeOnEastToWestTimezoneChanges) {
+                       DISABLED_BedtimeOnEastToWestTimezoneChanges) {
   LogInChildAndSetupClockWithTime("3 Jan 2018 8:00:00 GMT+1300");
   ScreenLockerTester().Lock();
 
@@ -797,7 +803,7 @@
 }
 
 // Tests if call the observers for usage time limit warning.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, CallObservers) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DISABLED_CallObservers) {
   if (!is_feature_enabled_)
     return;
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 PST");
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher_apitest.cc b/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher_apitest.cc
new file mode 100644
index 0000000..206abdd
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher_apitest.cc
@@ -0,0 +1,111 @@
+// 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 "base/bind.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/printing/cups_print_job.h"
+#include "chrome/browser/chromeos/printing/cups_print_job_manager_factory.h"
+#include "chrome/browser/chromeos/printing/test_cups_print_job_manager.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/policy_test_utils.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "extensions/test/extension_test_message_listener.h"
+#include "extensions/test/result_catcher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+namespace {
+
+constexpr char kUpdateManifestPath[] =
+    "/extensions/api_test/printing_metrics/update_manifest.xml";
+// The managed_storage extension has a key defined in its manifest, so that
+// its extension ID is well-known and the policy system can push policies for
+// the extension.
+constexpr char kTestExtensionID[] = "cmgkkmeeoiceijkpmaabbmpgnkpaaela";
+
+std::unique_ptr<KeyedService> BuildTestCupsPrintJobManager(
+    content::BrowserContext* context) {
+  return std::make_unique<chromeos::TestCupsPrintJobManager>(
+      Profile::FromBrowserContext(context));
+}
+
+}  // namespace
+
+class PrintJobFinishedEventDispatcherApiTest : public ExtensionApiTest {
+ public:
+  PrintJobFinishedEventDispatcherApiTest() {}
+  ~PrintJobFinishedEventDispatcherApiTest() override = default;
+
+ protected:
+  void SetUpInProcessBrowserTestFixture() override {
+    // Init the user policy provider.
+    EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_))
+        .WillRepeatedly(testing::Return(true));
+    policy_provider_.SetAutoRefresh();
+    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
+        &policy_provider_);
+    will_create_browser_context_services_subscription_ =
+        BrowserContextDependencyManager::GetInstance()
+            ->RegisterWillCreateBrowserContextServicesCallbackForTesting(
+                base::BindRepeating(&PrintJobFinishedEventDispatcherApiTest::
+                                        OnWillCreateBrowserContextServices,
+                                    base::Unretained(this)));
+    ExtensionApiTest::SetUpInProcessBrowserTestFixture();
+  }
+
+  policy::MockConfigurationPolicyProvider policy_provider_;
+
+ private:
+  void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
+    chromeos::CupsPrintJobManagerFactory::GetInstance()->SetTestingFactory(
+        context, base::BindRepeating(&BuildTestCupsPrintJobManager));
+  }
+
+  std::unique_ptr<
+      base::CallbackList<void(content::BrowserContext*)>::Subscription>
+      will_create_browser_context_services_subscription_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrintJobFinishedEventDispatcherApiTest);
+};
+
+IN_PROC_BROWSER_TEST_F(PrintJobFinishedEventDispatcherApiTest,
+                       OnPrintJobFinished) {
+  constexpr char kTitle[] = "title";
+  const int kPagesNumber = 3;
+  policy_test_utils::SetUpEmbeddedTestServer(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  policy_test_utils::SetExtensionInstallForcelistPolicy(
+      kTestExtensionID, embedded_test_server()->GetURL(kUpdateManifestPath),
+      profile(), &policy_provider_);
+
+  const Extension* extension =
+      ExtensionRegistry::Get(profile())->enabled_extensions().GetByID(
+          kTestExtensionID);
+  ASSERT_TRUE(extension);
+
+  ResultCatcher catcher;
+  Browser* const new_browser = CreateBrowser(profile());
+  ui_test_utils::NavigateToURL(
+      new_browser, extension->GetResourceURL("on_print_job_finished.html"));
+
+  std::unique_ptr<chromeos::CupsPrintJob> print_job =
+      std::make_unique<chromeos::CupsPrintJob>(
+          chromeos::Printer(), /*job_id=*/0, kTitle, kPagesNumber,
+          ::printing::PrintJob::Source::PRINT_PREVIEW,
+          /*source_id=*/"", chromeos::printing::proto::PrintSettings());
+  chromeos::TestCupsPrintJobManager* print_job_manager =
+      static_cast<chromeos::TestCupsPrintJobManager*>(
+          chromeos::CupsPrintJobManagerFactory::GetForBrowserContext(
+              browser()->profile()));
+  print_job_manager->CreatePrintJob(print_job.get());
+  print_job_manager->CancelPrintJob(print_job.get());
+
+  ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_apitest.cc b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_apitest.cc
new file mode 100644
index 0000000..30dc92d1
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_apitest.cc
@@ -0,0 +1,139 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/printing/cups_print_job.h"
+#include "chrome/browser/chromeos/printing/cups_print_job_manager_factory.h"
+#include "chrome/browser/chromeos/printing/history/print_job_history_service_factory.h"
+#include "chrome/browser/chromeos/printing/history/test_print_job_history_service_observer.h"
+#include "chrome/browser/chromeos/printing/test_cups_print_job_manager.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/policy_test_utils.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "extensions/test/extension_test_message_listener.h"
+#include "extensions/test/result_catcher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+namespace {
+
+constexpr char kUpdateManifestPath[] =
+    "/extensions/api_test/printing_metrics/update_manifest.xml";
+// The managed_storage extension has a key defined in its manifest, so that
+// its extension ID is well-known and the policy system can push policies for
+// the extension.
+constexpr char kTestExtensionID[] = "cmgkkmeeoiceijkpmaabbmpgnkpaaela";
+
+std::unique_ptr<KeyedService> BuildTestCupsPrintJobManager(
+    content::BrowserContext* context) {
+  return std::make_unique<chromeos::TestCupsPrintJobManager>(
+      Profile::FromBrowserContext(context));
+}
+
+}  // namespace
+
+class PrintingMetricsApiTest : public ExtensionApiTest {
+ public:
+  PrintingMetricsApiTest() {}
+  ~PrintingMetricsApiTest() override = default;
+
+ protected:
+  void SetUpInProcessBrowserTestFixture() override {
+    // Init the user policy provider.
+    EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_))
+        .WillRepeatedly(testing::Return(true));
+    policy_provider_.SetAutoRefresh();
+    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
+        &policy_provider_);
+    will_create_browser_context_services_subscription_ =
+        BrowserContextDependencyManager::GetInstance()
+            ->RegisterWillCreateBrowserContextServicesCallbackForTesting(
+                base::BindRepeating(
+                    &PrintingMetricsApiTest::OnWillCreateBrowserContextServices,
+                    base::Unretained(this)));
+    ExtensionApiTest::SetUpInProcessBrowserTestFixture();
+  }
+
+  policy::MockConfigurationPolicyProvider policy_provider_;
+
+ private:
+  void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
+    chromeos::CupsPrintJobManagerFactory::GetInstance()->SetTestingFactory(
+        context, base::BindRepeating(&BuildTestCupsPrintJobManager));
+  }
+
+  std::unique_ptr<
+      base::CallbackList<void(content::BrowserContext*)>::Subscription>
+      will_create_browser_context_services_subscription_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrintingMetricsApiTest);
+};
+
+IN_PROC_BROWSER_TEST_F(PrintingMetricsApiTest, GetPrintJobs) {
+  constexpr char kTitle[] = "title";
+  const int kPagesNumber = 3;
+
+  policy_test_utils::SetUpEmbeddedTestServer(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  policy_test_utils::SetExtensionInstallForcelistPolicy(
+      kTestExtensionID, embedded_test_server()->GetURL(kUpdateManifestPath),
+      profile(), &policy_provider_);
+
+  const Extension* extension =
+      ExtensionRegistry::Get(profile())->enabled_extensions().GetByID(
+          kTestExtensionID);
+  ASSERT_TRUE(extension);
+
+  base::RunLoop run_loop;
+  chromeos::TestPrintJobHistoryServiceObserver observer(
+      chromeos::PrintJobHistoryServiceFactory::GetForBrowserContext(
+          browser()->profile()),
+      run_loop.QuitClosure());
+
+  std::unique_ptr<chromeos::CupsPrintJob> print_job =
+      std::make_unique<chromeos::CupsPrintJob>(
+          chromeos::Printer(), /*job_id=*/0, kTitle, kPagesNumber,
+          ::printing::PrintJob::Source::PRINT_PREVIEW,
+          /*source_id=*/"", chromeos::printing::proto::PrintSettings());
+  chromeos::TestCupsPrintJobManager* print_job_manager =
+      static_cast<chromeos::TestCupsPrintJobManager*>(
+          chromeos::CupsPrintJobManagerFactory::GetForBrowserContext(
+              browser()->profile()));
+  print_job_manager->CreatePrintJob(print_job.get());
+  print_job_manager->CancelPrintJob(print_job.get());
+  run_loop.Run();
+
+  Browser* const new_browser = CreateBrowser(profile());
+  SetCustomArg(kTitle);
+  extensions::ResultCatcher catcher;
+  ui_test_utils::NavigateToURL(
+      new_browser, extension->GetResourceURL("get_print_jobs.html"));
+  ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
+
+// Ensure that extensions that are not pre-installed by policy throw an install
+// warning if they request the printingMetrics permission in the manifest and
+// that such extensions don't see the chrome.printingMetrics namespace.
+IN_PROC_BROWSER_TEST_F(PrintingMetricsApiTest, IsRestrictedToPolicyExtension) {
+  ASSERT_TRUE(RunExtensionSubtest("printing_metrics", "api_not_available.html",
+                                  kFlagIgnoreManifestWarnings));
+
+  base::FilePath extension_path =
+      test_data_dir_.AppendASCII("printing_metrics");
+  extensions::ExtensionRegistry* registry =
+      extensions::ExtensionRegistry::Get(profile());
+  const extensions::Extension* extension =
+      GetExtensionByPath(registry->enabled_extensions(), extension_path);
+  ASSERT_FALSE(extension->install_warnings().empty());
+  EXPECT_EQ("'printingMetrics' is not allowed for specified install location.",
+            extension->install_warnings()[0].message);
+}
+
+}  // namespace extensions
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 1dc98c9..35505b5 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
@@ -134,12 +134,6 @@
              IDS_FILE_BROWSER_DRIVE_RECENT_COLLECTION_LABEL);
   SET_STRING("DRIVE_SHARED_WITH_ME_COLLECTION_LABEL",
              IDS_FILE_BROWSER_DRIVE_SHARED_WITH_ME_COLLECTION_LABEL);
-  SET_STRING("DRIVE_SHARE_TYPE_CAN_COMMENT",
-             IDS_FILE_BROWSER_DRIVE_SHARE_TYPE_CAN_COMMENT);
-  SET_STRING("DRIVE_SHARE_TYPE_CAN_EDIT",
-             IDS_FILE_BROWSER_DRIVE_SHARE_TYPE_CAN_EDIT);
-  SET_STRING("DRIVE_SHARE_TYPE_CAN_VIEW",
-             IDS_FILE_BROWSER_DRIVE_SHARE_TYPE_CAN_VIEW);
   SET_STRING("DRIVE_SPACE_AVAILABLE_LONG",
              IDS_FILE_BROWSER_DRIVE_SPACE_AVAILABLE_LONG);
   SET_STRING("DRIVE_VISIT_DRIVE_GOOGLE_COM",
@@ -664,14 +658,6 @@
              IDS_MULTIPART_DEVICE_UNSUPPORTED_DEFAULT_MESSAGE);
   SET_STRING("MULTIPART_DEVICE_UNSUPPORTED_MESSAGE",
              IDS_MULTIPART_DEVICE_UNSUPPORTED_MESSAGE);
-  SET_STRING("MULTI_PROFILE_SHARE_DIALOG_MESSAGE",
-             IDS_FILE_BROWSER_MULTI_PROFILE_SHARE_DIALOG_MESSAGE);
-  SET_STRING("MULTI_PROFILE_SHARE_DIALOG_MESSAGE_PLURAL",
-             IDS_FILE_BROWSER_MULTI_PROFILE_SHARE_DIALOG_MESSAGE_PLURAL);
-  SET_STRING("MULTI_PROFILE_SHARE_DIALOG_TITLE",
-             IDS_FILE_BROWSER_MULTI_PROFILE_SHARE_DIALOG_TITLE);
-  SET_STRING("MULTI_PROFILE_SHARE_DIALOG_TITLE_PLURAL",
-             IDS_FILE_BROWSER_MULTI_PROFILE_SHARE_DIALOG_TITLE_PLURAL);
   SET_STRING("NAME_COLUMN_LABEL", IDS_FILE_BROWSER_NAME_COLUMN_LABEL);
   SET_STRING("EMPTY_FOLDER", IDS_FILE_BROWSER_EMPTY_FOLDER);
   SET_STRING("NEW_FOLDER_BUTTON_LABEL",
diff --git a/chrome/browser/chromeos/secure_channel/secure_channel_client_provider.cc b/chrome/browser/chromeos/secure_channel/secure_channel_client_provider.cc
index a75b2d8..803c2c91 100644
--- a/chrome/browser/chromeos/secure_channel/secure_channel_client_provider.cc
+++ b/chrome/browser/chromeos/secure_channel/secure_channel_client_provider.cc
@@ -30,7 +30,7 @@
     }()};
 
     mojo::PendingRemote<mojom::SecureChannel> channel;
-    (*instance)->BindRequest(channel.InitWithNewPipeAndPassReceiver());
+    (*instance)->BindReceiver(channel.InitWithNewPipeAndPassReceiver());
     secure_channel_client_ =
         SecureChannelClientImpl::Factory::Get()->BuildInstance(
             std::move(channel));
diff --git a/chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.cc b/chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.cc
index bec8af6..634dbc2 100644
--- a/chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.cc
+++ b/chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.cc
@@ -17,6 +17,7 @@
 #include "components/ownership/mock_owner_key_util.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -121,6 +122,8 @@
     OwnerSettingsServiceChromeOS::UpdateDeviceSettings(path, *value, settings);
     CHECK(settings.SerializeToString(data.mutable_policy_value()));
     CHECK(device_settings_cache::Store(data, g_browser_process->local_state()));
+    g_browser_process->local_state()->CommitPendingWrite(base::DoNothing(),
+                                                         base::DoNothing());
   }
 }
 
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index 57220d4..ae21c2c 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -177,9 +177,6 @@
     logging_state_active_ = active;
   }
 
-  void AutofillUsernameAndPasswordDataReceived(
-      const autofill::FormsPredictionsMap& predictions) override {}
-
   // Records whether SetLoggingState() gets called.
   bool called_set_logging_state_;
   // Records data received via SetLoggingState() call.
diff --git a/chrome/browser/password_manager/credential_leak_controller_android.cc b/chrome/browser/password_manager/credential_leak_controller_android.cc
index 13d6a95a..46efed6f 100644
--- a/chrome/browser/password_manager/credential_leak_controller_android.cc
+++ b/chrome/browser/password_manager/credential_leak_controller_android.cc
@@ -78,7 +78,7 @@
 }
 
 gfx::Range CredentialLeakControllerAndroid::GetDescriptionBoldRange() const {
-  return leak_dialog_utils::GetChangePasswordBoldRange(leak_type_);
+  return leak_dialog_utils::GetChangePasswordBoldRange(leak_type_, origin_);
 }
 
 bool CredentialLeakControllerAndroid::ShouldCheckPasswords() const {
diff --git a/chrome/browser/policy/profile_policy_connector.cc b/chrome/browser/policy/profile_policy_connector.cc
index faf302d..288f7efd 100644
--- a/chrome/browser/policy/profile_policy_connector.cc
+++ b/chrome/browser/policy/profile_policy_connector.cc
@@ -92,6 +92,24 @@
 };
 
 }  // namespace internal
+
+namespace {
+// Returns the PolicyService that holds device-wide policies.
+PolicyService* GetDeviceWidePolicyService() {
+  BrowserPolicyConnectorChromeOS* browser_policy_connector =
+      g_browser_process->platform_part()->browser_policy_connector_chromeos();
+  return browser_policy_connector->GetPolicyService();
+}
+
+// Returns the ProxyPolicyProvider which is used to forward primary Profile
+// policies into the device-wide PolicyService.
+ProxyPolicyProvider* GetProxyPolicyProvider() {
+  BrowserPolicyConnectorChromeOS* browser_policy_connector =
+      g_browser_process->platform_part()->browser_policy_connector_chromeos();
+  return browser_policy_connector->GetGlobalUserCloudPolicyProvider();
+}
+}  // namespace
+
 #endif  // defined(OS_CHROMEOS)
 
 ProfilePolicyConnector::ProfilePolicyConnector() {}
@@ -174,22 +192,40 @@
   }
 #endif
 
-  policy_service_ = std::make_unique<PolicyServiceImpl>(policy_providers_);
-
 #if defined(OS_CHROMEOS)
-  ConfigurationPolicyProvider* user_policy_delegate = nullptr;
-  if (is_primary_user_) {
-    user_policy_delegate = configuration_policy_provider
-                               ? configuration_policy_provider
-                               : special_user_policy_provider_.get();
+  ConfigurationPolicyProvider* user_policy_delegate_candidate =
+      configuration_policy_provider ? configuration_policy_provider
+                                    : special_user_policy_provider_.get();
+
+  // Only proxy primary user policies to the device_wide policy service if all
+  // of the following are true:
+  // (*) This ProfilePolicyConnector has been created for the primary user.
+  // (*) There is a policy provider for this profile. Note that for unmanaged
+  //     users, |user_policy_delegate_candidate| will be nullptr.
+  // (*) The ProxyPolicyProvider is actually used by the device-wide policy
+  //     service. This may not be the case  e.g. in tests that use
+  //     bBrowserPolicyConnectorBase::SetPolicyProviderForTesting.
+  if (is_primary_user_ && user_policy_delegate_candidate &&
+      GetDeviceWidePolicyService()->HasProvider(GetProxyPolicyProvider())) {
+    GetProxyPolicyProvider()->SetDelegate(user_policy_delegate_candidate);
+
+    // When proxying primary user policies to the device-wide PolicyService,
+    // delay signaling that initialization is complete until the policies have
+    // propagated. See CreatePolicyServiceWithInitializationThrottled for
+    // details.
+    policy_service_ = CreatePolicyServiceWithInitializationThrottled(
+        policy_providers_, user_policy_delegate_candidate);
+  } else {
+    policy_service_ = std::make_unique<PolicyServiceImpl>(policy_providers_);
   }
-  if (user_policy_delegate)
-    SetGlobalUserPolicyDelegate(user_policy_delegate);
-#endif
+#else   // defined(OS_CHROMEOS)
+  policy_service_ = std::make_unique<PolicyServiceImpl>(policy_providers_);
+#endif  // defined(OS_CHROMEOS)
 }
 
 void ProfilePolicyConnector::InitForTesting(
     std::unique_ptr<PolicyService> service) {
+  DCHECK(!policy_service_);
   policy_service_ = std::move(service);
 }
 
@@ -200,7 +236,7 @@
 void ProfilePolicyConnector::Shutdown() {
 #if defined(OS_CHROMEOS)
   if (is_primary_user_)
-    SetGlobalUserPolicyDelegate(nullptr);
+    GetProxyPolicyProvider()->SetDelegate(nullptr);
 
   if (special_user_policy_provider_)
     special_user_policy_provider_->Shutdown();
@@ -256,41 +292,33 @@
 }
 
 #if defined(OS_CHROMEOS)
-void ProfilePolicyConnector::SetGlobalUserPolicyDelegate(
+std::unique_ptr<PolicyService>
+ProfilePolicyConnector::CreatePolicyServiceWithInitializationThrottled(
+    const std::vector<ConfigurationPolicyProvider*>& policy_providers,
     ConfigurationPolicyProvider* user_policy_delegate) {
-  BrowserPolicyConnectorChromeOS* browser_policy_connector =
-      g_browser_process->platform_part()->browser_policy_connector_chromeos();
-  PolicyService* device_wide_policy_service =
-      browser_policy_connector->GetPolicyService();
-  ProxyPolicyProvider* proxy_policy_provider =
-      browser_policy_connector->GetGlobalUserCloudPolicyProvider();
+  DCHECK(user_policy_delegate);
 
-  // The ProxyPolicyProvider may be available from |browser_policy_connector|
-  // but not actually used by the |device_wide_policy_service| in tests (e.g. if
-  // BrowserPolicyConnectorBase::SetPolicyProviderForTesting has been used).
-  if (!device_wide_policy_service->HasProvider(proxy_policy_provider))
-    return;
+  auto policy_service =
+      PolicyServiceImpl::CreateWithThrottledInitialization(policy_providers);
 
-  if (!user_policy_delegate) {
-    proxy_policy_provider->SetDelegate(nullptr);
-    return;
-  }
-
-  policy_service_->SetInitializationThrottled(true);
   // base::Unretained is OK for |this| because
   // |proxied_policies_propagated_watcher_| is guaranteed not to call its
-  // callback after it has been destroyed.
+  // callback after it has been destroyed. base::Unretained is also OK for
+  // |policy_service.get()| because it will be owned by |*this| and is never
+  // explicitly destroyed.
   proxied_policies_propagated_watcher_ =
       std::make_unique<internal::ProxiedPoliciesPropagatedWatcher>(
-          device_wide_policy_service, proxy_policy_provider,
+          GetDeviceWidePolicyService(), GetProxyPolicyProvider(),
           user_policy_delegate,
           base::BindOnce(&ProfilePolicyConnector::OnProxiedPoliciesPropagated,
-                         base::Unretained(this)));
-  proxy_policy_provider->SetDelegate(user_policy_delegate);
+                         base::Unretained(this),
+                         base::Unretained(policy_service.get())));
+  return std::move(policy_service);
 }
 
-void ProfilePolicyConnector::OnProxiedPoliciesPropagated() {
-  policy_service_->SetInitializationThrottled(false);
+void ProfilePolicyConnector::OnProxiedPoliciesPropagated(
+    PolicyServiceImpl* policy_service) {
+  policy_service->UnthrottleInitialization();
   // Do not delete |proxied_policies_propagated_watcher_| synchronously, as the
   // PolicyService it is observing is expected to be iterating its observer
   // list.
diff --git a/chrome/browser/policy/profile_policy_connector.h b/chrome/browser/policy/profile_policy_connector.h
index 5e3f8ea..4c5ec61f 100644
--- a/chrome/browser/policy/profile_policy_connector.h
+++ b/chrome/browser/policy/profile_policy_connector.h
@@ -27,6 +27,7 @@
 class CloudPolicyStore;
 class ConfigurationPolicyProvider;
 class PolicyService;
+class PolicyServiceImpl;
 class SchemaRegistry;
 class ChromeBrowserPolicyConnector;
 
@@ -81,22 +82,31 @@
 
 #if defined(OS_CHROMEOS)
   // On Chrome OS, primary Profile user policies are forwarded to the
-  // device-global PolicyService[1] using a ProxyPolicyProvider. This function
-  // sets up that forwarding, using |user_policy_delegate| as the policy source.
-  // It also delays signaling that |policy_service_| is initialized until the
-  // policies provided by |user_policy_delegate| have propagated to the
-  // device-wide PolicyService[1]. This is done so that code that runs early on
-  // Profile initialization can rely on the device-wide PolicyService[1] and
-  // local state Preferences[2] respecting the proxied primary user policies.
+  // device-global PolicyService[1] using a ProxyPolicyProvider.
+  // When that is done, signaling that |policy_service_| is initialized should
+  // be delayed until the policies provided by |user_policy_delegate| have
+  // propagated to the device-wide PolicyService[1]. This is done so that code
+  // that runs early on Profile initialization can rely on the device-wide
+  // PolicyService[1] and local state Preferences[2] respecting the proxied
+  // primary user policies.
+  //
+  // This function starts watching for the propagation to happen by creating a
+  // |ProxiedPoliciesPropagatedWatcher| and creates a PolicyService that
+  // only signals that it is initilalized when the
+  // |proxied_policies_propagated_watcher_| has fired.
   //
   // [1] i.e. g_browser_process->policy_service()
   // [2] i.e. g_browser_process->local_state()
-  void SetGlobalUserPolicyDelegate(
+  std::unique_ptr<PolicyService> CreatePolicyServiceWithInitializationThrottled(
+      const std::vector<ConfigurationPolicyProvider*>& policy_providers,
       ConfigurationPolicyProvider* user_policy_delegate);
 
   // Called when primary user policies that are proxied to the device-wide
   // PolicyService have propagated.
-  void OnProxiedPoliciesPropagated();
+  // |policy_service| is passed here because UnthrottleInitialization is
+  // implemented on PolicyServiceImpl, but the |policy_service_| class member is
+  // a PolicyService for testability.
+  void OnProxiedPoliciesPropagated(PolicyServiceImpl* policy_service);
 
   // Some of the user policy configuration affects browser global state, and
   // can only come from one Profile. |is_primary_user_| is true if this
diff --git a/chrome/browser/resources/chromeos/login/oobe_a11y_option.css b/chrome/browser/resources/chromeos/login/oobe_a11y_option.css
index b970fad..af042db 100644
--- a/chrome/browser/resources/chromeos/login/oobe_a11y_option.css
+++ b/chrome/browser/resources/chromeos/login/oobe_a11y_option.css
@@ -15,6 +15,7 @@
 
 cr-toggle {
   align-self: center;
+  margin-inline-end: 12px;
 }
 
 /* Line heights of 20 pixels title (#titleContainer) and 20 pixels value
diff --git a/chrome/browser/resources/chromeos/zip_archiver/js/compressor.js b/chrome/browser/resources/chromeos/zip_archiver/js/compressor.js
index 73adf4c5..9bdc57f 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/js/compressor.js
+++ b/chrome/browser/resources/chromeos/zip_archiver/js/compressor.js
@@ -479,14 +479,7 @@
     entry.file(
         (file) => {
           this.file_ = file;
-          chrome.fileManagerPrivate.ensureFileDownloaded(entry, () => {
-            if (chrome.runtime.lastError) {
-              console.error(chrome.runtime.lastError.message);
-              this.onErrorInternal_();
-              return;
-            }
-            readFileChunk();
-          });
+          readFileChunk();
         },
         (error) => {
           console.error(error);
diff --git a/chrome/browser/resources/hangout_services/OWNERS b/chrome/browser/resources/hangout_services/OWNERS
index db6c6c29..f283b346 100644
--- a/chrome/browser/resources/hangout_services/OWNERS
+++ b/chrome/browser/resources/hangout_services/OWNERS
@@ -1,2 +1,2 @@
-bemasc@chromium.org
+eladalon@chromium.org
 grunell@chromium.org
diff --git a/chrome/browser/resources/local_ntp/externs.js b/chrome/browser/resources/local_ntp/externs.js
index 4ec7d82..8532d933 100644
--- a/chrome/browser/resources/local_ntp/externs.js
+++ b/chrome/browser/resources/local_ntp/externs.js
@@ -383,6 +383,8 @@
  *  SearchBoxBindings::GetObjectTemplateBuilder()
  */
 window.chrome.embeddedSearch.searchBox;
+/** @param {number} line */
+window.chrome.embeddedSearch.searchBox.deleteAutocompleteMatch;
 window.chrome.embeddedSearch.searchBox.isKeyCaptureEnabled;
 window.chrome.embeddedSearch.searchBox.paste;
 window.chrome.embeddedSearch.searchBox.rtl;
@@ -420,7 +422,7 @@
  * @typedef {{
  *   input: string,
  *   matches: !Array<!AutocompleteMatch>,
- *   status: !AutocompleteResultStatus
+ *   status: !AutocompleteResultStatus,
  * }}
  */
 let AutocompleteResult;
@@ -428,6 +430,17 @@
 /** @type {function(!AutocompleteResult):void} */
 window.chrome.embeddedSearch.searchBox.onqueryautocompletedone;
 
+/**
+ * @typedef {{
+ *   success: boolean,
+ *   matches: !Array<!AutocompleteMatch>,
+ * }}
+ */
+let DeleteAutocompleteMatchResult;
+
+/** @type {function(!DeleteAutocompleteMatchResult):void} */
+window.chrome.embeddedSearch.searchBox.ondeleteautocompletematch;
+
 /**************************** Translated Strings *****************************/
 
 /**
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index f68a076..0f7ba8d5 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -231,6 +231,7 @@
 const REALBOX_KEYDOWN_HANDLED_KEYS = [
   'ArrowDown',
   'ArrowUp',
+  'Delete',
   'Enter',
   'Escape',
   'PageDown',
@@ -755,6 +756,7 @@
       realboxWrapper.addEventListener('focusout', onRealboxWrapperFocusOut);
 
       searchboxApiHandle.onqueryautocompletedone = onQueryAutocompleteDone;
+      searchboxApiHandle.ondeleteautocompletematch = onDeleteAutocompleteMatch;
 
       if (!iframesAndVoiceSearchDisabledForTesting) {
         speech.init(
@@ -1062,6 +1064,33 @@
   ntpApiHandle.logEvent(LOG_TYPE.NTP_CUSTOMIZE_SHORTCUT_DONE);
 }
 
+/** @param {!DeleteAutocompleteMatchResult} result */
+function onDeleteAutocompleteMatch(result) {
+  if (!result.success) {
+    return;
+  }
+
+  const matchEls = Array.from($(IDS.REALBOX_MATCHES).children);
+  const selected = matchEls.findIndex(matchEl => {
+    return matchEl.classList.contains(CLASSES.SELECTED);
+  });
+
+  populateAutocompleteMatches(result.matches);
+
+  if (result.matches.length === 0) {
+    return;
+  }
+
+  const newMatchEls = Array.from($(IDS.REALBOX_MATCHES).children);
+  const newSelected = Math.min(newMatchEls.length - 1, selected);
+  selectMatchEl(newMatchEls[newSelected]);
+  updateRealboxOutput({
+    moveCursorToEnd: true,
+    inline: '',
+    text: autocompleteMatches[newSelected].fillIntoEdit,
+  });
+}
+
 /**
  * Callback for embeddedSearch.newTabPage.ondeletecustomlinkdone. Called when
  * the custom link was successfully deleted. Shows the "Shortcut deleted"
@@ -1112,74 +1141,22 @@
     return;  // Stale or skipped result; ignore.
   }
 
-  const realboxMatchesEl = document.createElement('div');
+  populateAutocompleteMatches(result.matches);
 
-  for (const [i, match] of result.matches.entries()) {
-    const matchEl = document.createElement('a');
-    matchEl.href = match.destinationUrl;
-
-    let iconClass;
-    if (match.isSearchType) {
-      const isSearchHistory = SEARCH_HISTORY_MATCH_TYPES.includes(match.type);
-      iconClass = isSearchHistory ? CLASSES.CLOCK_ICON : CLASSES.SEARCH_ICON;
-    } else {
-      // TODO(crbug.com/997229): use chrome://favicon/<url> when perms allow.
-      iconClass = CLASSES.URL_ICON;
-    }
-    const icon = document.createElement('div');
-    icon.classList.add(assert(iconClass));
-    matchEl.appendChild(icon);
-
-    const contentsEls =
-        renderMatchClassifications(match.contents, match.contentsClass);
-    const descriptionEls = [];
-    const separatorEls = [];
-
-    if (match.description) {
-      descriptionEls.push(...renderMatchClassifications(
-          match.description, match.descriptionClass));
-      separatorEls.push(document.createTextNode(
-          configData.translatedStrings.realboxSeparator));
-    }
-
-    const layout = match.swapContentsAndDescription ?
-        [descriptionEls, separatorEls, contentsEls] :
-        [contentsEls, separatorEls, descriptionEls];
-
-    for (const col of layout) {
-      col.forEach(colEl => matchEl.appendChild(colEl));
-    }
-
-    realboxMatchesEl.append(matchEl);
+  if (result.matches.length === 0) {
+    return;
   }
 
-  const hasMatches = result.matches.length > 0;
-  if (hasMatches) {
-    realboxMatchesEl.firstElementChild.classList.add(CLASSES.SELECTED);
-  }
+  $(IDS.REALBOX_MATCHES).firstElementChild.classList.add(CLASSES.SELECTED);
 
-  $(IDS.REALBOX_MATCHES).remove();
-  realboxMatchesEl.id = IDS.REALBOX_MATCHES;
-
-  const realboxWrapper = $(IDS.REALBOX_INPUT_WRAPPER);
-  realboxWrapper.appendChild(realboxMatchesEl);
-
-  realboxWrapper.classList.toggle(CLASSES.SHOW_MATCHES, hasMatches);
-
-  if (hasMatches) {
-    realboxWrapper.addEventListener('keydown', onRealboxKeyDown);
-
-    // If the user is deleting content, don't quickly re-suggest the same
-    // output.
-    if (!isDeleting) {
-      const first = result.matches[0];
-      if (first.allowedToBeDefaultMatch && first.inlineAutocompletion) {
-        updateRealboxOutput({inline: first.inlineAutocompletion});
-      }
+  // If the user is deleting content, don't quickly re-suggest the same
+  // output.
+  if (!isDeleting) {
+    const first = result.matches[0];
+    if (first.allowedToBeDefaultMatch && first.inlineAutocompletion) {
+      updateRealboxOutput({inline: first.inlineAutocompletion});
     }
   }
-
-  autocompleteMatches = result.matches;
 }
 
 /** @param {!Event} e */
@@ -1193,8 +1170,8 @@
   }
 
   const matchEls = Array.from($(IDS.REALBOX_MATCHES).children);
-  const selected = matchEls.findIndex(match => {
-    return match.classList.contains(CLASSES.SELECTED);
+  const selected = matchEls.findIndex(matchEl => {
+    return matchEl.classList.contains(CLASSES.SELECTED);
   });
 
   const selectedMatch = autocompleteMatches[selected];
@@ -1237,15 +1214,21 @@
     return;
   }
 
-  const hasMods = e.ctrlKey || e.metaKey || e.shiftKey;
-  if (hasMods && key !== 'Enter') {
+  const matchEls = Array.from($(IDS.REALBOX_MATCHES).children);
+  const selected = matchEls.findIndex(matchEl => {
+    return matchEl.classList.contains(CLASSES.SELECTED);
+  });
+
+  if (key === 'Delete' && e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) {
+    window.chrome.embeddedSearch.searchBox.deleteAutocompleteMatch(selected);
+    e.preventDefault();
     return;
   }
 
-  const matchEls = Array.from($(IDS.REALBOX_MATCHES).children);
-  const selected = matchEls.findIndex(match => {
-    return match.classList.contains(CLASSES.SELECTED);
-  });
+  const hasMods = e.altKey || e.ctrlKey || e.metaKey || e.shiftKey;
+  if (hasMods && key !== 'Enter') {
+    return;
+  }
 
   if (key === 'Enter') {
     if (matchEls[selected]) {
@@ -1387,6 +1370,67 @@
 }
 
 /**
+ * @param {!Array<!AutocompleteMatch>} matches
+ */
+function populateAutocompleteMatches(matches) {
+  const realboxMatchesEl = document.createElement('div');
+
+  for (const [i, match] of matches.entries()) {
+    const matchEl = document.createElement('a');
+    matchEl.href = match.destinationUrl;
+
+    let iconClass;
+    if (match.isSearchType) {
+      const isSearchHistory = SEARCH_HISTORY_MATCH_TYPES.includes(match.type);
+      iconClass = isSearchHistory ? CLASSES.CLOCK_ICON : CLASSES.SEARCH_ICON;
+    } else {
+      // TODO(crbug.com/997229): use chrome://favicon/<url> when perms allow.
+      iconClass = CLASSES.URL_ICON;
+    }
+    const icon = document.createElement('div');
+    icon.classList.add(assert(iconClass));
+    matchEl.appendChild(icon);
+
+    const contentsEls =
+        renderMatchClassifications(match.contents, match.contentsClass);
+    const descriptionEls = [];
+    const separatorEls = [];
+
+    if (match.description) {
+      descriptionEls.push(...renderMatchClassifications(
+          match.description, match.descriptionClass));
+      separatorEls.push(document.createTextNode(
+          configData.translatedStrings.realboxSeparator));
+    }
+
+    const layout = match.swapContentsAndDescription ?
+        [descriptionEls, separatorEls, contentsEls] :
+        [contentsEls, separatorEls, descriptionEls];
+
+    for (const col of layout) {
+      col.forEach(colEl => matchEl.appendChild(colEl));
+    }
+
+    realboxMatchesEl.append(matchEl);
+  }
+
+  $(IDS.REALBOX_MATCHES).remove();
+  realboxMatchesEl.id = IDS.REALBOX_MATCHES;
+
+  const realboxWrapper = $(IDS.REALBOX_INPUT_WRAPPER);
+  realboxWrapper.appendChild(realboxMatchesEl);
+
+  const hasMatches = matches.length > 0;
+  realboxWrapper.classList.toggle(CLASSES.SHOW_MATCHES, hasMatches);
+
+  if (hasMatches) {
+    realboxWrapper.addEventListener('keydown', onRealboxKeyDown);
+  }
+
+  autocompleteMatches = matches;
+}
+
+/**
  * @param {!Element} element
  * @param {!Array<string>} keys
  * @param {!function(Event)} handler
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk.cc b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
index 80542c2c..b77b8a5 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk.cc
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
@@ -346,13 +346,6 @@
   g_type_class_unref(g_type_class_ref(gtk_tree_view_get_type()));
   g_type_class_unref(g_type_class_ref(gtk_window_get_type()));
 
-  // Add the web native theme as an observer to stay in sync with dark mode,
-  // high contrast, and preferred color scheme changes.
-  color_scheme_observer_ =
-      std::make_unique<NativeTheme::ColorSchemeNativeThemeObserver>(
-          NativeTheme::GetInstanceForWeb());
-  AddObserver(color_scheme_observer_.get());
-
   OnThemeChanged(gtk_settings_get_default(), nullptr);
 }
 
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk.h b/chrome/browser/ui/libgtkui/native_theme_gtk.h
index e5bb437..302fbe8 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk.h
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk.h
@@ -86,11 +86,6 @@
 
   ScopedCssProvider theme_css_override_;
 
-  // Used to notify the web native theme of changes to dark mode, high
-  // contrast, and preferred color scheme.
-  std::unique_ptr<NativeTheme::ColorSchemeNativeThemeObserver>
-      color_scheme_observer_;
-
   DISALLOW_COPY_AND_ASSIGN(NativeThemeGtk);
 };
 
diff --git a/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.cc b/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.cc
index 713e652d..b029f07 100644
--- a/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.cc
+++ b/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.cc
@@ -91,7 +91,7 @@
 
 gfx::Range CredentialLeakDialogControllerImpl::GetChangePasswordBoldRange()
     const {
-  return leak_dialog_utils::GetChangePasswordBoldRange(leak_type_);
+  return leak_dialog_utils::GetChangePasswordBoldRange(leak_type_, origin_);
 }
 
 void CredentialLeakDialogControllerImpl::ResetDialog() {
diff --git a/chrome/browser/ui/passwords/credential_leak_dialog_utils.cc b/chrome/browser/ui/passwords/credential_leak_dialog_utils.cc
index 52088a5..6ed1e39 100644
--- a/chrome/browser/ui/passwords/credential_leak_dialog_utils.cc
+++ b/chrome/browser/ui/passwords/credential_leak_dialog_utils.cc
@@ -21,6 +21,15 @@
 using password_manager::metrics_util::LeakDialogType;
 
 namespace leak_dialog_utils {
+namespace {
+
+// Formats the |origin| to a human-friendly url string.
+base::string16 GetFormattedUrl(const GURL& origin) {
+  return url_formatter::FormatUrlForSecurityDisplay(
+      origin, url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
+}
+
+}  // namespace
 
 base::string16 GetAcceptButtonLabel(CredentialLeakType leak_type) {
   return l10n_util::GetStringUTF16(
@@ -33,9 +42,7 @@
 
 base::string16 GetDescription(CredentialLeakType leak_type,
                               const GURL& origin) {
-  const auto formatted = url_formatter::FormatOriginForSecurityDisplay(
-      url::Origin::Create(origin),
-      url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
+  const base::string16 formatted = GetFormattedUrl(origin);
   if (!ShouldCheckPasswords(leak_type)) {
     std::vector<size_t> offsets;
     base::string16 bold_message = l10n_util::GetStringUTF16(
@@ -82,16 +89,17 @@
   return GURL(value);
 }
 
-gfx::Range GetChangePasswordBoldRange(CredentialLeakType leak_type) {
+gfx::Range GetChangePasswordBoldRange(CredentialLeakType leak_type,
+                                      const GURL& origin) {
   if (ShouldCheckPasswords(leak_type))
     return gfx::Range();
 
   std::vector<size_t> offsets;
   const base::string16 bold_message = l10n_util::GetStringUTF16(
       IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_BOLD_MESSAGE);
-  const base::string16 change_password_message =
-      l10n_util::GetStringFUTF16(IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE,
-                                 bold_message, base::string16(), &offsets);
+  const base::string16 change_password_message = l10n_util::GetStringFUTF16(
+      IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE, bold_message,
+      GetFormattedUrl(origin), &offsets);
   return offsets.empty()
              ? gfx::Range()
              : gfx::Range(offsets[0], offsets[0] + bold_message.length());
diff --git a/chrome/browser/ui/passwords/credential_leak_dialog_utils.h b/chrome/browser/ui/passwords/credential_leak_dialog_utils.h
index 9074247..54bd37b 100644
--- a/chrome/browser/ui/passwords/credential_leak_dialog_utils.h
+++ b/chrome/browser/ui/passwords/credential_leak_dialog_utils.h
@@ -44,7 +44,8 @@
 // Returns the range of the bold part of the leak dialog message when
 // credentials were leaked only on current site.
 gfx::Range GetChangePasswordBoldRange(
-    password_manager::CredentialLeakType leak_type);
+    password_manager::CredentialLeakType leak_type,
+    const GURL& origin);
 
 }  // namespace leak_dialog_utils
 
diff --git a/chrome/browser/ui/search/search_ipc_router.cc b/chrome/browser/ui/search/search_ipc_router.cc
index 62d38f9..3fb3ffe1 100644
--- a/chrome/browser/ui/search/search_ipc_router.cc
+++ b/chrome/browser/ui/search/search_ipc_router.cc
@@ -469,6 +469,18 @@
   delegate_->BlocklistPromo(promo_id);
 }
 
+void SearchIPCRouter::DeleteAutocompleteMatch(
+    uint8_t line,
+    chrome::mojom::EmbeddedSearch::DeleteAutocompleteMatchCallback callback) {
+  if (!policy_->ShouldProcessDeleteAutocompleteMatch()) {
+    std::move(callback).Run(chrome::mojom::DeleteAutocompleteMatchResult::New(
+        false, std::vector<chrome::mojom::AutocompleteMatchPtr>()));
+    return;
+  }
+
+  delegate_->DeleteAutocompleteMatch(line, std::move(callback));
+}
+
 void SearchIPCRouter::set_delegate_for_testing(Delegate* delegate) {
   DCHECK(delegate);
   delegate_ = delegate;
diff --git a/chrome/browser/ui/search/search_ipc_router.h b/chrome/browser/ui/search/search_ipc_router.h
index 2d63753..e56d10f 100644
--- a/chrome/browser/ui/search/search_ipc_router.h
+++ b/chrome/browser/ui/search/search_ipc_router.h
@@ -163,6 +163,11 @@
     virtual void StopAutocomplete(bool clear_result) = 0;
 
     virtual void BlocklistPromo(const std::string& promo_id) = 0;
+
+    virtual void DeleteAutocompleteMatch(
+        uint8_t line,
+        chrome::mojom::EmbeddedSearch::DeleteAutocompleteMatchCallback
+            callback) = 0;
   };
 
   // An interface to be implemented by consumers of SearchIPCRouter objects to
@@ -204,6 +209,7 @@
     virtual bool ShouldProcessQueryAutocomplete(bool is_active_tab) = 0;
     virtual bool ShouldProcessStopAutocomplete(bool is_active_tab) = 0;
     virtual bool ShouldProcessBlocklistPromo() = 0;
+    virtual bool ShouldProcessDeleteAutocompleteMatch() = 0;
   };
 
   // Creates chrome::mojom::EmbeddedSearchClient connections on request.
@@ -315,6 +321,10 @@
       override;
   void StopAutocomplete(bool clear_result) override;
   void BlocklistPromo(const std::string& promo_id) override;
+  void DeleteAutocompleteMatch(
+      uint8_t line,
+      chrome::mojom::EmbeddedSearch::DeleteAutocompleteMatchCallback callback)
+      override;
   void set_embedded_search_client_factory_for_testing(
       std::unique_ptr<EmbeddedSearchClientFactory> factory) {
     embedded_search_client_factory_ = std::move(factory);
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_impl.cc b/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
index b5fc5f1b..1305e63 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
+++ b/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
@@ -145,3 +145,7 @@
 bool SearchIPCRouterPolicyImpl::ShouldProcessBlocklistPromo() {
   return !is_incognito_ && search::IsInstantNTP(web_contents_);
 }
+
+bool SearchIPCRouterPolicyImpl::ShouldProcessDeleteAutocompleteMatch() {
+  return !is_incognito_ && search::IsInstantNTP(web_contents_);
+}
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_impl.h b/chrome/browser/ui/search/search_ipc_router_policy_impl.h
index 6ad1d4f..7c3a83b7 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_impl.h
+++ b/chrome/browser/ui/search/search_ipc_router_policy_impl.h
@@ -57,6 +57,7 @@
   bool ShouldProcessQueryAutocomplete(bool is_active_tab) override;
   bool ShouldProcessStopAutocomplete(bool is_active_tab) override;
   bool ShouldProcessBlocklistPromo() override;
+  bool ShouldProcessDeleteAutocompleteMatch() override;
 
   // Used by unit tests.
   void set_is_incognito(bool is_incognito) {
diff --git a/chrome/browser/ui/search/search_ipc_router_unittest.cc b/chrome/browser/ui/search/search_ipc_router_unittest.cc
index 4228b9c1..2aa416d 100644
--- a/chrome/browser/ui/search/search_ipc_router_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_unittest.cc
@@ -104,6 +104,11 @@
   MOCK_METHOD0(OnRevertThemeChanges, void());
   MOCK_METHOD0(OnConfirmThemeChanges, void());
   MOCK_METHOD2(
+      DeleteAutocompleteMatch,
+      void(uint8_t line,
+           chrome::mojom::EmbeddedSearch::DeleteAutocompleteMatchCallback
+               callback));
+  MOCK_METHOD2(
       QueryAutocomplete,
       void(const base::string16& input,
            chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback));
@@ -146,6 +151,7 @@
   MOCK_METHOD1(ShouldProcessQueryAutocomplete, bool(bool));
   MOCK_METHOD1(ShouldProcessStopAutocomplete, bool(bool));
   MOCK_METHOD0(ShouldProcessBlocklistPromo, bool());
+  MOCK_METHOD0(ShouldProcessDeleteAutocompleteMatch, bool());
 };
 
 class MockEmbeddedSearchClientFactory
@@ -1098,3 +1104,18 @@
 
   GetSearchIPCRouter().BlocklistPromo(std::string());
 }
+
+TEST_F(SearchIPCRouterTest, IgnoreDeleteAutocompleteMatch) {
+  NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
+  SetupMockDelegateAndPolicy();
+  MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
+  EXPECT_CALL(*mock_delegate(), DeleteAutocompleteMatch(_, _)).Times(0);
+  EXPECT_CALL(*policy, ShouldProcessDeleteAutocompleteMatch())
+      .Times(1)
+      .WillOnce(Return(false));
+
+  base::MockCallback<
+      chrome::mojom::EmbeddedSearch::DeleteAutocompleteMatchCallback>
+      callback;
+  GetSearchIPCRouter().DeleteAutocompleteMatch(0u, callback.Get());
+}
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index b171945..bb0340e3 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -61,6 +61,38 @@
 
 namespace {
 
+std::vector<chrome::mojom::AutocompleteMatchPtr> CreateAutocompleteMatches(
+    const AutocompleteResult& result) {
+  std::vector<chrome::mojom::AutocompleteMatchPtr> matches;
+  for (const AutocompleteMatch& match : result) {
+    chrome::mojom::AutocompleteMatchPtr mojom_match =
+        chrome::mojom::AutocompleteMatch::New();
+    mojom_match->allowed_to_be_default_match =
+        match.allowed_to_be_default_match;
+    mojom_match->contents = match.contents;
+    for (const auto& contents_class : match.contents_class) {
+      mojom_match->contents_class.push_back(
+          chrome::mojom::ACMatchClassification::New(contents_class.offset,
+                                                    contents_class.style));
+    }
+    mojom_match->description = match.description;
+    for (const auto& description_class : match.description_class) {
+      mojom_match->description_class.push_back(
+          chrome::mojom::ACMatchClassification::New(description_class.offset,
+                                                    description_class.style));
+    }
+    mojom_match->destination_url = match.destination_url.spec();
+    mojom_match->fill_into_edit = match.fill_into_edit;
+    mojom_match->inline_autocompletion = match.inline_autocompletion;
+    mojom_match->is_search_type = AutocompleteMatch::IsSearchType(match.type);
+    mojom_match->swap_contents_and_description =
+        match.swap_contents_and_description;
+    mojom_match->type = AutocompleteMatchType::ToString(match.type);
+    matches.push_back(std::move(mojom_match));
+  }
+  return matches;
+}
+
 bool IsCacheableNTP(content::WebContents* contents) {
   content::NavigationEntry* entry =
       contents->GetController().GetLastCommittedEntry();
@@ -447,37 +479,10 @@
   if (!autocomplete_controller_->done() || !query_autocomplete_callback_)
     return;
 
-  std::vector<chrome::mojom::AutocompleteMatchPtr> matches;
-  for (const AutocompleteMatch& match : autocomplete_controller_->result()) {
-    chrome::mojom::AutocompleteMatchPtr mojom_match =
-        chrome::mojom::AutocompleteMatch::New();
-    mojom_match->allowed_to_be_default_match =
-        match.allowed_to_be_default_match;
-    mojom_match->contents = match.contents;
-    for (const auto& contents_class : match.contents_class) {
-      mojom_match->contents_class.push_back(
-          chrome::mojom::ACMatchClassification::New(contents_class.offset,
-                                                    contents_class.style));
-    }
-    mojom_match->description = match.description;
-    for (const auto& description_class : match.description_class) {
-      mojom_match->description_class.push_back(
-          chrome::mojom::ACMatchClassification::New(description_class.offset,
-                                                    description_class.style));
-    }
-    mojom_match->destination_url = match.destination_url.spec();
-    mojom_match->fill_into_edit = match.fill_into_edit;
-    mojom_match->inline_autocompletion = match.inline_autocompletion;
-    mojom_match->is_search_type = AutocompleteMatch::IsSearchType(match.type);
-    mojom_match->swap_contents_and_description =
-        match.swap_contents_and_description;
-    mojom_match->type = AutocompleteMatchType::ToString(match.type);
-    matches.push_back(std::move(mojom_match));
-  }
-
   std::move(query_autocomplete_callback_)
       .Run(chrome::mojom::AutocompleteResult::New(
-          autocomplete_controller_->input().text(), std::move(matches),
+          autocomplete_controller_->input().text(),
+          CreateAutocompleteMatches(autocomplete_controller_->result()),
           chrome::mojom::AutocompleteResultStatus::SUCCESS));
 }
 
@@ -600,6 +605,28 @@
   autocomplete_controller_->Start(autocomplete_input);
 }
 
+void SearchTabHelper::DeleteAutocompleteMatch(
+    uint8_t line,
+    chrome::mojom::EmbeddedSearch::DeleteAutocompleteMatchCallback callback) {
+  bool success = false;
+  std::vector<chrome::mojom::AutocompleteMatchPtr> matches;
+
+  if (search::DefaultSearchProviderIsGoogle(profile()) &&
+      autocomplete_controller_ &&
+      autocomplete_controller_->result().size() > line) {
+    const auto& match = autocomplete_controller_->result().match_at(line);
+    if (match.SupportsDeletion()) {
+      success = true;
+      autocomplete_controller_->Stop(false);
+      autocomplete_controller_->DeleteMatch(match);
+      matches = CreateAutocompleteMatches(autocomplete_controller_->result());
+    }
+  }
+
+  std::move(callback).Run(chrome::mojom::DeleteAutocompleteMatchResult::New(
+      success, std::move(matches)));
+}
+
 void SearchTabHelper::StopAutocomplete(bool clear_result) {
   if (!autocomplete_controller_) {
     return;
diff --git a/chrome/browser/ui/search/search_tab_helper.h b/chrome/browser/ui/search/search_tab_helper.h
index 4c6cf701..0e98b68 100644
--- a/chrome/browser/ui/search/search_tab_helper.h
+++ b/chrome/browser/ui/search/search_tab_helper.h
@@ -141,6 +141,10 @@
       const base::string16& input,
       chrome::mojom::EmbeddedSearch::QueryAutocompleteCallback callback)
       override;
+  void DeleteAutocompleteMatch(
+      uint8_t line,
+      chrome::mojom::EmbeddedSearch::DeleteAutocompleteMatchCallback callback)
+      override;
   void StopAutocomplete(bool clear_result) override;
   void BlocklistPromo(const std::string& promo_id) override;
 
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index ee20f12..731d064 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -69,8 +69,12 @@
   // color_utils::GetColorWithMaxContrast()/IsDark() aren't used here because
   // they switch based on the Chrome light/dark endpoints, while we want to use
   // the system native behavior below.
-  return color_utils::GetLuma(background_color) < 128 ? SK_ColorWHITE
-                                                      : SK_ColorBLACK;
+  const auto windows_luma = [](SkColor c) {
+    return 0.25f * SkColorGetR(c) + 0.625f * SkColorGetG(c) +
+           0.125f * SkColorGetB(c);
+  };
+  return windows_luma(background_color) <= 128.0f ? SK_ColorWHITE
+                                                  : SK_ColorBLACK;
 }
 
 GlassBrowserFrameView::GlassBrowserFrameView(BrowserFrame* frame,
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
index f9f7150..8f07faac 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
@@ -70,6 +70,15 @@
                  .GetNumberOfProfiles() == 1;
 }
 
+int GetIconSizeForNonTouchUi() {
+  // Note that the non-touchable icon size is larger than the default to
+  // make the avatar icon easier to read.
+  if (base::FeatureList::IsEnabled(features::kAnimatedAvatarButton)) {
+    return 22;
+  }
+  return 20;
+}
+
 }  // namespace
 
 AvatarToolbarButton::AvatarToolbarButton(Browser* browser)
@@ -414,10 +423,9 @@
 
 gfx::ImageSkia AvatarToolbarButton::GetAvatarIcon(
     const gfx::Image& gaia_image) const {
-  // Note that the non-touchable icon size is larger than the default to
-  // make the avatar icon easier to read.
-  const int icon_size =
-      ui::MaterialDesignController::touch_ui() ? kDefaultTouchableIconSize : 20;
+  const int icon_size = ui::MaterialDesignController::touch_ui()
+                            ? kDefaultTouchableIconSize
+                            : GetIconSizeForNonTouchUi();
 
   SkColor icon_color =
       GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
@@ -532,9 +540,12 @@
 }
 
 void AvatarToolbarButton::SetInsets() {
-  // In non-touch mode we use a larger-than-normal icon size for avatars as 16dp
-  // is hard to read for user avatars, so we need to set corresponding insets.
-  gfx::Insets layout_insets(ui::MaterialDesignController::touch_ui() ? 0 : -2);
+  // In non-touch mode we use a larger-than-normal icon size for avatars so we
+  // need to compensate it by smaller insets.
+  gfx::Insets layout_insets(
+      ui::MaterialDesignController::touch_ui()
+          ? 0
+          : (kDefaultIconSize - GetIconSizeForNonTouchUi()) / 2);
 
   SetLayoutInsetDelta(layout_insets);
 }
diff --git a/chrome/browser/ui/web_applications/web_app_badging_browsertest.cc b/chrome/browser/ui/web_applications/web_app_badging_browsertest.cc
index 211d920..38ffca2a 100644
--- a/chrome/browser/ui/web_applications/web_app_badging_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_badging_browsertest.cc
@@ -35,12 +35,14 @@
 
     GURL cross_site_frame_url =
         cross_origin_https_server_.GetURL("/ssl/google.html");
+    cross_site_app_id_ = InstallPWA(cross_site_frame_url);
+
     // Note: The url for the cross site frame is embedded in the query string.
     GURL app_url = https_server()->GetURL(
         "/ssl/page_with_in_scope_and_cross_site_frame.html?url=" +
         cross_site_frame_url.spec());
-    AppId app_id = InstallPWA(app_url);
-    content::WebContents* web_contents = OpenApplication(app_id);
+    main_app_id_ = InstallPWA(app_url);
+    content::WebContents* web_contents = OpenApplication(main_app_id_);
     // There should be exactly 3 frames:
     // 1) The main frame.
     // 2) A cross site frame, on |cross_site_frame_url|.
@@ -85,13 +87,15 @@
     // make asserting the result of a badge change easier.
     int total_changes = delegate_->cleared_app_badges().size() +
                         delegate_->set_app_badges().size();
-    EXPECT_LE(total_changes, 1);
+    ASSERT_EQ(total_changes, 1);
 
-    // A change is a failure if it doesn't set or clear any badges.
-    change_failed_ = total_changes == 0;
-    was_cleared_ = delegate_->cleared_app_badges().size();
+    if (delegate_->cleared_app_badges().size()) {
+      changed_app_id_ = delegate_->cleared_app_badges()[0];
+      was_cleared_ = true;
+    }
 
     if (delegate_->set_app_badges().size() == 1) {
+      changed_app_id_ = delegate_->set_app_badges()[0].first;
       last_badge_content_ = delegate_->set_app_badges()[0].second;
       was_flagged_ = last_badge_content_ == base::nullopt;
     }
@@ -104,30 +108,34 @@
                                           RenderFrameHost* on) {
     was_cleared_ = false;
     was_flagged_ = false;
-    change_failed_ = false;
+    changed_app_id_ = base::nullopt;
     last_badge_content_ = base::nullopt;
     awaiter_ = std::make_unique<base::RunLoop>();
     delegate_->ResetBadges();
 
     ASSERT_TRUE(content::ExecuteScript(on, script));
 
-    if (was_cleared_ || was_flagged_ || change_failed_ ||
-        last_badge_content_ != base::nullopt)
+    if (was_cleared_ || was_flagged_ || changed_app_id_ || last_badge_content_)
       return;
 
     awaiter_->Run();
   }
 
+  const web_app::AppId& main_app_id() { return main_app_id_; }
+  const web_app::AppId& cross_site_app_id() { return cross_site_app_id_; }
+
   RenderFrameHost* main_frame_;
   RenderFrameHost* in_scope_frame_;
   RenderFrameHost* cross_site_frame_;
 
   bool was_cleared_ = false;
   bool was_flagged_ = false;
-  bool change_failed_ = false;
+  base::Optional<web_app::AppId> changed_app_id_ = base::nullopt;
   base::Optional<uint64_t> last_badge_content_ = base::nullopt;
 
  private:
+  web_app::AppId main_app_id_;
+  web_app::AppId cross_site_app_id_;
   std::unique_ptr<base::RunLoop> awaiter_;
   badging::TestBadgeManagerDelegate* delegate_;
   net::EmbeddedTestServer cross_origin_https_server_;
@@ -136,20 +144,21 @@
 // Tests that the badge for the main frame is not affected by changing the badge
 // of a cross site subframe.
 IN_PROC_BROWSER_TEST_P(WebAppBadgingBrowserTest,
-                       BadgeCannotBeChangedFromCrossSiteFrame) {
-  // Clearing from cross site frame should be a no-op.
+                       CrossSiteFrameCannotChangeMainFrameBadge) {
+  // Clearing from cross site frame should affect only the cross site app.
   ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.clear()",
                                      cross_site_frame_);
-  ASSERT_FALSE(was_cleared_);
+  ASSERT_TRUE(was_cleared_);
   ASSERT_FALSE(was_flagged_);
-  ASSERT_TRUE(change_failed_);
+  ASSERT_EQ(cross_site_app_id(), changed_app_id_);
 
-  // Setting from cross site frame should be a no-op.
+  // Setting from cross site frame should affect only the cross site app.
   ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.set(77)",
                                      cross_site_frame_);
   ASSERT_FALSE(was_cleared_);
   ASSERT_FALSE(was_flagged_);
-  ASSERT_TRUE(change_failed_);
+  ASSERT_EQ(77u, last_badge_content_);
+  ASSERT_EQ(cross_site_app_id(), changed_app_id_);
 }
 
 // Tests that setting the badge to an integer will be propagated across
@@ -158,7 +167,7 @@
   ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.set(99)", main_frame_);
   ASSERT_FALSE(was_cleared_);
   ASSERT_FALSE(was_flagged_);
-  ASSERT_FALSE(change_failed_);
+  ASSERT_EQ(main_app_id(), changed_app_id_);
   ASSERT_EQ(base::Optional<uint64_t>(99u), last_badge_content_);
 }
 
@@ -168,13 +177,13 @@
   ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.set(55)", main_frame_);
   ASSERT_FALSE(was_cleared_);
   ASSERT_FALSE(was_flagged_);
-  ASSERT_FALSE(change_failed_);
+  ASSERT_EQ(main_app_id(), changed_app_id_);
   ASSERT_EQ(base::Optional<uint64_t>(55u), last_badge_content_);
 
   ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.clear()", main_frame_);
   ASSERT_TRUE(was_cleared_);
   ASSERT_FALSE(was_flagged_);
-  ASSERT_FALSE(change_failed_);
+  ASSERT_EQ(main_app_id(), changed_app_id_);
   ASSERT_EQ(base::nullopt, last_badge_content_);
 }
 
@@ -184,7 +193,7 @@
   ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.set(0)", main_frame_);
   ASSERT_TRUE(was_cleared_);
   ASSERT_FALSE(was_flagged_);
-  ASSERT_FALSE(change_failed_);
+  ASSERT_EQ(main_app_id(), changed_app_id_);
   ASSERT_EQ(base::nullopt, last_badge_content_);
 }
 
@@ -193,7 +202,7 @@
   ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.set()", main_frame_);
   ASSERT_FALSE(was_cleared_);
   ASSERT_TRUE(was_flagged_);
-  ASSERT_FALSE(change_failed_);
+  ASSERT_EQ(main_app_id(), changed_app_id_);
   ASSERT_EQ(base::nullopt, last_badge_content_);
 }
 
@@ -204,14 +213,14 @@
                                      in_scope_frame_);
   ASSERT_FALSE(was_cleared_);
   ASSERT_TRUE(was_flagged_);
-  ASSERT_FALSE(change_failed_);
+  ASSERT_EQ(main_app_id(), changed_app_id_);
   ASSERT_EQ(base::nullopt, last_badge_content_);
 
   ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.clear()",
                                      in_scope_frame_);
   ASSERT_TRUE(was_cleared_);
   ASSERT_FALSE(was_flagged_);
-  ASSERT_FALSE(change_failed_);
+  ASSERT_EQ(main_app_id(), changed_app_id_);
   ASSERT_EQ(base::nullopt, last_badge_content_);
 }
 
diff --git a/chrome/browser/web_applications/test/test_web_app_registry_controller.cc b/chrome/browser/web_applications/test/test_web_app_registry_controller.cc
index 845e569..b2e3590 100644
--- a/chrome/browser/web_applications/test/test_web_app_registry_controller.cc
+++ b/chrome/browser/web_applications/test/test_web_app_registry_controller.cc
@@ -7,6 +7,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
 #include "chrome/browser/web_applications/test/test_web_app_database_factory.h"
+#include "chrome/browser/web_applications/web_app_registry_update.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 
 namespace web_app {
@@ -33,6 +34,23 @@
   run_loop.Run();
 }
 
+void TestWebAppRegistryController::RegisterApp(
+    std::unique_ptr<WebApp> web_app) {
+  ScopedRegistryUpdate update(sync_bridge_.get());
+  update->CreateApp(std::move(web_app));
+}
+
+void TestWebAppRegistryController::UnregisterApp(const AppId& app_id) {
+  ScopedRegistryUpdate update(sync_bridge_.get());
+  update->DeleteApp(app_id);
+}
+
+void TestWebAppRegistryController::UnregisterAll() {
+  ScopedRegistryUpdate update(sync_bridge_.get());
+  for (const AppId& app_id : registrar().GetAppIds())
+    update->DeleteApp(app_id);
+}
+
 void TestWebAppRegistryController::DestroySubsystems() {
   mutable_registrar_.reset();
   sync_bridge_.reset();
diff --git a/chrome/browser/web_applications/test/test_web_app_registry_controller.h b/chrome/browser/web_applications/test/test_web_app_registry_controller.h
index cf92ff1..7d97770 100644
--- a/chrome/browser/web_applications/test/test_web_app_registry_controller.h
+++ b/chrome/browser/web_applications/test/test_web_app_registry_controller.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "components/sync/model/mock_model_type_change_processor.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -17,6 +18,7 @@
 
 class TestWebAppDatabaseFactory;
 class WebAppSyncBridge;
+class WebApp;
 
 class TestWebAppRegistryController {
  public:
@@ -29,6 +31,10 @@
   // metadata.
   void Init();
 
+  void RegisterApp(std::unique_ptr<WebApp> web_app);
+  void UnregisterApp(const AppId& app_id);
+  void UnregisterAll();
+
   void DestroySubsystems();
 
   TestWebAppDatabaseFactory& database_factory() { return *database_factory_; }
diff --git a/chrome/browser/web_applications/web_app_database.cc b/chrome/browser/web_applications/web_app_database.cc
index cc8edc7..9156bb4 100644
--- a/chrome/browser/web_applications/web_app_database.cc
+++ b/chrome/browser/web_applications/web_app_database.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/web_applications/proto/web_app.pb.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_database_factory.h"
+#include "chrome/browser/web_applications/web_app_registry_update.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/model/model_error.h"
 #include "components/sync/protocol/web_app_specifics.pb.h"
@@ -38,32 +39,45 @@
                           weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
-void WebAppDatabase::WriteWebApps(AppsToWrite apps,
-                                  CompletionCallback callback) {
+void WebAppDatabase::BeginTransaction() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(opened_);
 
-  BeginTransaction();
+  DCHECK(!write_batch_);
+  write_batch_ = store_->CreateWriteBatch();
+}
 
-  for (const WebApp* web_app : apps) {
+void WebAppDatabase::CommitTransaction(const RegistryUpdateData& update_data,
+                                       CompletionCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(opened_);
+
+  DCHECK(write_batch_);
+  DCHECK(!update_data.IsEmpty());
+
+  for (const std::unique_ptr<WebApp>& web_app : update_data.apps_to_create) {
     auto proto = CreateWebAppProto(*web_app);
     write_batch_->WriteData(web_app->app_id(), proto->SerializeAsString());
   }
 
-  CommitTransaction(std::move(callback));
-}
-
-void WebAppDatabase::DeleteWebApps(std::vector<AppId> app_ids,
-                                   CompletionCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(opened_);
-
-  BeginTransaction();
-
-  for (auto& app_id : app_ids)
+  for (const AppId& app_id : update_data.apps_to_delete)
     write_batch_->DeleteData(app_id);
 
-  CommitTransaction(std::move(callback));
+  for (const WebApp* web_app : update_data.apps_to_update) {
+    auto proto = CreateWebAppProto(*web_app);
+    write_batch_->WriteData(web_app->app_id(), proto->SerializeAsString());
+  }
+
+  store_->CommitWriteBatch(
+      std::move(write_batch_),
+      base::BindOnce(&WebAppDatabase::OnDataWritten,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  write_batch_.reset();
+}
+
+void WebAppDatabase::CancelTransaction() {
+  DCHECK(write_batch_);
+  write_batch_.reset();
 }
 
 // static
@@ -279,21 +293,4 @@
   return web_app;
 }
 
-void WebAppDatabase::BeginTransaction() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!write_batch_);
-  write_batch_ = store_->CreateWriteBatch();
-}
-
-void WebAppDatabase::CommitTransaction(CompletionCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(write_batch_);
-
-  store_->CommitWriteBatch(
-      std::move(write_batch_),
-      base::BindOnce(&WebAppDatabase::OnDataWritten,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-  write_batch_.reset();
-}
-
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_database.h b/chrome/browser/web_applications/web_app_database.h
index 9ef4edf..881cd7c2 100644
--- a/chrome/browser/web_applications/web_app_database.h
+++ b/chrome/browser/web_applications/web_app_database.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/callback_forward.h"
-#include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
@@ -26,6 +25,7 @@
 class AbstractWebAppDatabaseFactory;
 class WebApp;
 class WebAppProto;
+struct RegistryUpdateData;
 
 // Exclusively used from the UI thread.
 class WebAppDatabase {
@@ -34,13 +34,15 @@
   ~WebAppDatabase();
 
   using RegistryOpenedCallback = base::OnceCallback<void(Registry registry)>;
-  using CompletionCallback = base::OnceCallback<void(bool success)>;
-  using AppsToWrite = base::flat_set<const WebApp*>;
-
   // Open existing or create new DB. Read all data and return it via callback.
   void OpenDatabase(RegistryOpenedCallback callback);
-  void WriteWebApps(AppsToWrite apps, CompletionCallback callback);
-  void DeleteWebApps(std::vector<AppId> app_ids, CompletionCallback callback);
+
+  using CompletionCallback = base::OnceCallback<void(bool success)>;
+  // There can be only 1 transaction at a time.
+  void BeginTransaction();
+  void CommitTransaction(const RegistryUpdateData& update_data,
+                         CompletionCallback callback);
+  void CancelTransaction();
 
   // Exposed for testing.
   static std::unique_ptr<WebAppProto> CreateWebAppProto(const WebApp& web_app);
@@ -63,9 +65,6 @@
   void OnDataWritten(CompletionCallback callback,
                      const base::Optional<syncer::ModelError>& error);
 
-  void BeginTransaction();
-  void CommitTransaction(CompletionCallback callback);
-
   std::unique_ptr<syncer::ModelTypeStore> store_;
   std::unique_ptr<syncer::ModelTypeStore::WriteBatch> write_batch_;
   AbstractWebAppDatabaseFactory* database_factory_;
diff --git a/chrome/browser/web_applications/web_app_database_unittest.cc b/chrome/browser/web_applications/web_app_database_unittest.cc
index 3f6e4c68..fa68a01 100644
--- a/chrome/browser/web_applications/web_app_database_unittest.cc
+++ b/chrome/browser/web_applications/web_app_database_unittest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/web_applications/test/web_app_test.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/browser/web_applications/web_app_registry_update.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "components/sync/model/model_type_store.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -132,10 +133,6 @@
     return controller().database_factory();
   }
 
-  WebAppDatabase& database() {
-    return controller().sync_bridge().database_for_testing();
-  }
-
   WebAppRegistrar& registrar() { return controller().registrar(); }
 
   WebAppRegistrarMutable& mutable_registrar() {
@@ -157,19 +154,19 @@
 
   auto app = CreateWebApp(base_url, 0);
   auto app_id = app->app_id();
-  sync_bridge().RegisterApp(std::move(app));
+  controller().RegisterApp(std::move(app));
   EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
 
   for (int i = 1; i <= num_apps; ++i) {
     auto extra_app = CreateWebApp(base_url, i);
-    sync_bridge().RegisterApp(std::move(extra_app));
+    controller().RegisterApp(std::move(extra_app));
   }
   EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
 
-  sync_bridge().UnregisterApp(app_id);
+  controller().UnregisterApp(app_id);
   EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
 
-  sync_bridge().UnregisterAll();
+  controller().UnregisterAll();
   EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
 }
 
@@ -180,37 +177,51 @@
   const int num_apps = 10;
   const std::string base_url = "https://example.com/path";
 
-  Registry registry;
-  WebAppDatabase::AppsToWrite apps_to_write;
-  std::vector<AppId> apps_to_delete;
+  RegistryUpdateData::AppsToCreate apps_to_create;
+  RegistryUpdateData::AppsToDelete apps_to_delete;
+  Registry expected_registry;
 
   for (int i = 0; i < num_apps; ++i) {
-    auto app = CreateWebApp(base_url, i);
-    apps_to_write.insert(app.get());
+    std::unique_ptr<WebApp> app = CreateWebApp(base_url, i);
     apps_to_delete.push_back(app->app_id());
-    registry.emplace(app->app_id(), std::move(app));
+    apps_to_create.push_back(std::move(app));
+
+    std::unique_ptr<WebApp> expected_app = CreateWebApp(base_url, i);
+    expected_registry.emplace(expected_app->app_id(), std::move(expected_app));
   }
 
   {
     base::RunLoop run_loop;
-    database().WriteWebApps(std::move(apps_to_write),
-                            base::BindLambdaForTesting([&](bool success) {
-                              EXPECT_TRUE(success);
-                              run_loop.Quit();
-                            }));
+
+    std::unique_ptr<WebAppRegistryUpdate> update = sync_bridge().BeginUpdate();
+
+    for (std::unique_ptr<WebApp>& web_app : apps_to_create)
+      update->CreateApp(std::move(web_app));
+
+    sync_bridge().CommitUpdate(std::move(update),
+                               base::BindLambdaForTesting([&](bool success) {
+                                 EXPECT_TRUE(success);
+                                 run_loop.Quit();
+                               }));
     run_loop.Run();
 
     Registry registry_written = database_factory().ReadRegistry();
-    EXPECT_TRUE(IsRegistryEqual(registry_written, registry));
+    EXPECT_TRUE(IsRegistryEqual(registry_written, expected_registry));
   }
 
   {
     base::RunLoop run_loop;
-    database().DeleteWebApps(std::move(apps_to_delete),
-                             base::BindLambdaForTesting([&](bool success) {
-                               EXPECT_TRUE(success);
-                               run_loop.Quit();
-                             }));
+
+    std::unique_ptr<WebAppRegistryUpdate> update = sync_bridge().BeginUpdate();
+
+    for (const AppId& app_id : apps_to_delete)
+      update->DeleteApp(app_id);
+
+    sync_bridge().CommitUpdate(std::move(update),
+                               base::BindLambdaForTesting([&](bool success) {
+                                 EXPECT_TRUE(success);
+                                 run_loop.Quit();
+                               }));
     run_loop.Run();
 
     Registry registry_deleted = database_factory().ReadRegistry();
@@ -252,7 +263,7 @@
   EXPECT_TRUE(app->scope().is_empty());
   EXPECT_FALSE(app->theme_color().has_value());
   EXPECT_TRUE(app->icons().empty());
-  sync_bridge().RegisterApp(std::move(app));
+  controller().RegisterApp(std::move(app));
 
   Registry registry = database_factory().ReadRegistry();
   EXPECT_EQ(1UL, registry.size());
@@ -298,7 +309,7 @@
   }
   app->SetIcons(std::move(icons));
 
-  sync_bridge().RegisterApp(std::move(app));
+  controller().RegisterApp(std::move(app));
 
   Registry registry = database_factory().ReadRegistry();
   EXPECT_EQ(1UL, registry.size());
diff --git a/chrome/browser/web_applications/web_app_icon_manager_unittest.cc b/chrome/browser/web_applications/web_app_icon_manager_unittest.cc
index 20dda12..f9c7f8d 100644
--- a/chrome/browser/web_applications/web_app_icon_manager_unittest.cc
+++ b/chrome/browser/web_applications/web_app_icon_manager_unittest.cc
@@ -116,7 +116,7 @@
 
   web_app->SetIcons(ListIcons(web_app->launch_url(), sizes_px));
 
-  sync_bridge().RegisterApp(std::move(web_app));
+  controller().RegisterApp(std::move(web_app));
 
   {
     base::RunLoop run_loop;
@@ -145,7 +145,7 @@
   icons.push_back({icon_url, icon_size_px});
   web_app->SetIcons(std::move(icons));
 
-  sync_bridge().RegisterApp(std::move(web_app));
+  controller().RegisterApp(std::move(web_app));
 
   // Request non-existing icon size.
   EXPECT_FALSE(
@@ -175,7 +175,7 @@
 
   web_app->SetIcons(ListIcons(web_app->launch_url(), sizes_px));
 
-  sync_bridge().RegisterApp(std::move(web_app));
+  controller().RegisterApp(std::move(web_app));
 
   {
     const bool icon_requested = icon_manager().ReadIcon(
@@ -210,7 +210,7 @@
 
   web_app->SetIcons(ListIcons(web_app->launch_url(), sizes_px));
 
-  sync_bridge().RegisterApp(std::move(web_app));
+  controller().RegisterApp(std::move(web_app));
 
   {
     const bool icon_requested = icon_manager().ReadSmallestIcon(
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index e69da88d..0ac80e1 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -131,7 +131,7 @@
 
   icon_manager_->WriteData(
       std::move(app_id), std::make_unique<WebApplicationInfo>(web_app_info),
-      base::BindOnce(&WebAppInstallFinalizer::OnDataWritten,
+      base::BindOnce(&WebAppInstallFinalizer::OnIconsDataWritten,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                      std::move(web_app)));
 }
@@ -150,23 +150,40 @@
   NOTIMPLEMENTED();
 }
 
-void WebAppInstallFinalizer::OnDataWritten(InstallFinalizedCallback callback,
-                                           std::unique_ptr<WebApp> web_app,
-                                           bool success) {
+void WebAppInstallFinalizer::OnIconsDataWritten(
+    InstallFinalizedCallback callback,
+    std::unique_ptr<WebApp> web_app,
+    bool success) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!success) {
     std::move(callback).Run(AppId(), InstallResultCode::kWriteDataFailed);
     return;
   }
 
+  std::unique_ptr<WebAppRegistryUpdate> update = sync_bridge_->BeginUpdate();
+
   AppId app_id = web_app->app_id();
+  update->CreateApp(std::move(web_app));
 
-  sync_bridge_->RegisterApp(std::move(web_app));
-  // TODO(loyso): NotifyWebAppInstalled should be a part of RegisterApp.
+  sync_bridge_->CommitUpdate(
+      std::move(update),
+      base::BindOnce(&WebAppInstallFinalizer::OnDatabaseCommitCompleted,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                     std::move(app_id)));
+}
+
+void WebAppInstallFinalizer::OnDatabaseCommitCompleted(
+    InstallFinalizedCallback callback,
+    const AppId& app_id,
+    bool success) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!success) {
+    std::move(callback).Run(AppId(), InstallResultCode::kWriteDataFailed);
+    return;
+  }
+
   registrar().NotifyWebAppInstalled(app_id);
-
-  std::move(callback).Run(std::move(app_id),
-                          InstallResultCode::kSuccessNewInstall);
+  std::move(callback).Run(app_id, InstallResultCode::kSuccessNewInstall);
 }
 
 bool WebAppInstallFinalizer::CanCreateOsShortcuts() const {
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.h b/chrome/browser/web_applications/web_app_install_finalizer.h
index 003669c..1dd26c7 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.h
+++ b/chrome/browser/web_applications/web_app_install_finalizer.h
@@ -44,9 +44,12 @@
   bool CanUserUninstallFromSync(const AppId& app_id) const override;
 
  private:
-  void OnDataWritten(InstallFinalizedCallback callback,
-                     std::unique_ptr<WebApp> web_app,
-                     bool success);
+  void OnIconsDataWritten(InstallFinalizedCallback callback,
+                          std::unique_ptr<WebApp> web_app,
+                          bool success);
+  void OnDatabaseCommitCompleted(InstallFinalizedCallback callback,
+                                 const AppId& app_id,
+                                 bool success);
 
   WebAppSyncBridge* sync_bridge_;
   WebAppIconManager* const icon_manager_;
diff --git a/chrome/browser/web_applications/web_app_registrar_unittest.cc b/chrome/browser/web_applications/web_app_registrar_unittest.cc
index b8374d7..b82ea24 100644
--- a/chrome/browser/web_applications/web_app_registrar_unittest.cc
+++ b/chrome/browser/web_applications/web_app_registrar_unittest.cc
@@ -73,14 +73,25 @@
   std::set<AppId> RegisterAppsForTesting(Registry registry) {
     std::set<AppId> ids;
 
+    ScopedRegistryUpdate update(&sync_bridge());
     for (auto& kv : registry) {
       ids.insert(kv.first);
-      sync_bridge().RegisterApp(std::move(kv.second));
+      update->CreateApp(std::move(kv.second));
     }
 
     return ids;
   }
 
+  void RegisterApp(std::unique_ptr<WebApp> web_app) {
+    controller().RegisterApp(std::move(web_app));
+  }
+
+  void UnregisterApp(const AppId& app_id) {
+    controller().UnregisterApp(app_id);
+  }
+
+  void UnregisterAll() { controller().UnregisterAll(); }
+
   AppId InitRegistrarWithApp(std::unique_ptr<WebApp> app) {
     DCHECK(registrar().is_empty());
 
@@ -175,7 +186,7 @@
   EXPECT_EQ(nullptr, registrar().GetAppById(app_id2));
   EXPECT_TRUE(registrar().is_empty());
 
-  sync_bridge().RegisterApp(std::move(web_app));
+  RegisterApp(std::move(web_app));
   EXPECT_TRUE(registrar().IsInstalled(app_id));
   const WebApp* app = registrar().GetAppById(app_id);
 
@@ -189,13 +200,13 @@
   EXPECT_EQ(nullptr, registrar().GetAppById(app_id2));
   EXPECT_FALSE(registrar().is_empty());
 
-  sync_bridge().RegisterApp(std::move(web_app2));
+  RegisterApp(std::move(web_app2));
   EXPECT_TRUE(registrar().IsInstalled(app_id2));
   const WebApp* app2 = registrar().GetAppById(app_id2);
   EXPECT_EQ(app_id2, app2->app_id());
   EXPECT_FALSE(registrar().is_empty());
 
-  sync_bridge().UnregisterApp(app_id);
+  UnregisterApp(app_id);
   EXPECT_FALSE(registrar().IsInstalled(app_id));
   EXPECT_EQ(nullptr, registrar().GetAppById(app_id));
   EXPECT_FALSE(registrar().is_empty());
@@ -205,7 +216,7 @@
   EXPECT_TRUE(registrar().IsInstalled(app_id2));
   EXPECT_EQ(app_id2, app2->app_id());
 
-  sync_bridge().UnregisterApp(app_id2);
+  UnregisterApp(app_id2);
   EXPECT_FALSE(registrar().IsInstalled(app_id2));
   EXPECT_EQ(nullptr, registrar().GetAppById(app_id2));
   EXPECT_TRUE(registrar().is_empty());
@@ -215,10 +226,10 @@
   controller().Init();
 
   auto web_app = CreateWebApp("https://example.com/path");
-  sync_bridge().RegisterApp(std::move(web_app));
+  RegisterApp(std::move(web_app));
 
   auto web_app2 = CreateWebApp("https://example.com/path2");
-  sync_bridge().RegisterApp(std::move(web_app2));
+  RegisterApp(std::move(web_app2));
 
   controller().DestroySubsystems();
 }
@@ -260,7 +271,7 @@
   EXPECT_TRUE(ids.empty());
 
   EXPECT_FALSE(registrar().is_empty());
-  sync_bridge().UnregisterAll();
+  UnregisterAll();
   EXPECT_TRUE(registrar().is_empty());
 }
 
@@ -271,18 +282,18 @@
   auto web_app = CreateWebApp("https://example.com/path");
   const AppId app_id = web_app->app_id();
 
-  sync_bridge().RegisterApp(std::move(web_app));
+  RegisterApp(std::move(web_app));
 
   EXPECT_EQ(101UL, database_factory().ReadAllAppIds().size());
   EXPECT_EQ(101UL, controller().mutable_registrar().registry().size());
 
   // Remove 1 app after Init.
-  sync_bridge().UnregisterApp(app_id);
+  UnregisterApp(app_id);
   EXPECT_EQ(100UL, controller().mutable_registrar().registry().size());
   EXPECT_EQ(100UL, database_factory().ReadAllAppIds().size());
 
   // Remove 100 apps after Init.
-  sync_bridge().UnregisterAll();
+  UnregisterAll();
   EXPECT_TRUE(database_factory().ReadAllAppIds().empty());
   EXPECT_TRUE(registrar().is_empty());
 }
@@ -311,7 +322,7 @@
   web_app->SetLaunchContainer(launch_container);
   web_app->SetIsLocallyInstalled(/*is_locally_installed*/ false);
 
-  sync_bridge().RegisterApp(std::move(web_app));
+  RegisterApp(std::move(web_app));
 
   EXPECT_EQ(name, registrar().GetAppShortName(app_id));
   EXPECT_EQ(description, registrar().GetAppDescription(app_id));
@@ -358,7 +369,7 @@
 
   auto app1 = CreateWebApp(app1_scope.spec());
   app1->SetScope(app1_scope);
-  sync_bridge().RegisterApp(std::move(app1));
+  RegisterApp(std::move(app1));
 
   in_scope = registrar().FindAppsInScope(origin_scope);
   EXPECT_THAT(in_scope, testing::UnorderedElementsAre(app1_id));
@@ -368,7 +379,7 @@
 
   auto app2 = CreateWebApp(app2_scope.spec());
   app2->SetScope(app2_scope);
-  sync_bridge().RegisterApp(std::move(app2));
+  RegisterApp(std::move(app2));
 
   in_scope = registrar().FindAppsInScope(origin_scope);
   EXPECT_THAT(in_scope, testing::UnorderedElementsAre(app1_id, app2_id));
@@ -381,7 +392,7 @@
 
   auto app3 = CreateWebApp(app3_scope.spec());
   app3->SetScope(app3_scope);
-  sync_bridge().RegisterApp(std::move(app3));
+  RegisterApp(std::move(app3));
 
   in_scope = registrar().FindAppsInScope(origin_scope);
   EXPECT_THAT(in_scope, testing::UnorderedElementsAre(app1_id, app2_id));
@@ -405,7 +416,7 @@
 
   auto app1 = CreateWebApp(app1_scope.spec());
   app1->SetScope(app1_scope);
-  sync_bridge().RegisterApp(std::move(app1));
+  RegisterApp(std::move(app1));
 
   base::Optional<AppId> app2_match =
       registrar().FindAppWithUrlInScope(app2_scope);
@@ -418,11 +429,11 @@
 
   auto app2 = CreateWebApp(app2_scope.spec());
   app2->SetScope(app2_scope);
-  sync_bridge().RegisterApp(std::move(app2));
+  RegisterApp(std::move(app2));
 
   auto app3 = CreateWebApp(app3_scope.spec());
   app3->SetScope(app3_scope);
-  sync_bridge().RegisterApp(std::move(app3));
+  RegisterApp(std::move(app3));
 
   base::Optional<AppId> origin_match =
       registrar().FindAppWithUrlInScope(origin_scope);
@@ -459,7 +470,7 @@
 
   // Implicit scope "https://example.com/app/"
   auto app1 = CreateWebApp(app1_launch.spec());
-  sync_bridge().RegisterApp(std::move(app1));
+  RegisterApp(std::move(app1));
 
   base::Optional<AppId> app2_match =
       registrar().FindAppWithUrlInScope(app2_page);
@@ -470,10 +481,10 @@
   EXPECT_FALSE(app3_match);
 
   auto app2 = CreateWebApp(app2_launch.spec());
-  sync_bridge().RegisterApp(std::move(app2));
+  RegisterApp(std::move(app2));
 
   auto app3 = CreateWebApp(app3_launch.spec());
-  sync_bridge().RegisterApp(std::move(app3));
+  RegisterApp(std::move(app3));
 
   base::Optional<AppId> app1_match =
       registrar().FindAppWithUrlInScope(app1_page);
@@ -501,14 +512,14 @@
   const GURL app3_launch("https://example.com/app/specific/launch3");
 
   auto app1 = CreateWebApp(app1_launch.spec());
-  sync_bridge().RegisterApp(std::move(app1));
+  RegisterApp(std::move(app1));
 
   auto app2 = CreateWebApp(app2_scope.spec());
   app2->SetScope(app2_scope);
-  sync_bridge().RegisterApp(std::move(app2));
+  RegisterApp(std::move(app2));
 
   auto app3 = CreateWebApp(app3_launch.spec());
-  sync_bridge().RegisterApp(std::move(app3));
+  RegisterApp(std::move(app3));
 
   base::Optional<AppId> app2_match =
       registrar().FindAppWithUrlInScope(app2_page);
diff --git a/chrome/browser/web_applications/web_app_registry_update.cc b/chrome/browser/web_applications/web_app_registry_update.cc
index d747b50d..9eb7aa2c 100644
--- a/chrome/browser/web_applications/web_app_registry_update.cc
+++ b/chrome/browser/web_applications/web_app_registry_update.cc
@@ -5,29 +5,59 @@
 #include "chrome/browser/web_applications/web_app_registry_update.h"
 
 #include "base/bind_helpers.h"
+#include "base/stl_util.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 
 namespace web_app {
 
+RegistryUpdateData::RegistryUpdateData() = default;
+
+RegistryUpdateData::~RegistryUpdateData() = default;
+
+bool RegistryUpdateData::IsEmpty() const {
+  return apps_to_create.empty() && apps_to_delete.empty() &&
+         apps_to_update.empty();
+}
+
 WebAppRegistryUpdate::WebAppRegistryUpdate(
     WebAppRegistrarMutable* mutable_registrar)
-    : mutable_registrar_(mutable_registrar) {}
+    : mutable_registrar_(mutable_registrar) {
+  DCHECK(mutable_registrar_);
+  update_data_ = std::make_unique<RegistryUpdateData>();
+}
 
 WebAppRegistryUpdate::~WebAppRegistryUpdate() = default;
 
+void WebAppRegistryUpdate::CreateApp(std::unique_ptr<WebApp> web_app) {
+  DCHECK(update_data_);
+  DCHECK(!web_app->app_id().empty());
+  DCHECK(!mutable_registrar_->GetAppById(web_app->app_id()));
+  DCHECK(!base::Contains(update_data_->apps_to_create, web_app));
+
+  update_data_->apps_to_create.push_back(std::move(web_app));
+}
+
+void WebAppRegistryUpdate::DeleteApp(const AppId& app_id) {
+  DCHECK(update_data_);
+  DCHECK(!app_id.empty());
+  DCHECK(mutable_registrar_->GetAppById(app_id));
+  DCHECK(!base::Contains(update_data_->apps_to_delete, app_id));
+
+  update_data_->apps_to_delete.push_back(app_id);
+}
+
 WebApp* WebAppRegistryUpdate::UpdateApp(const AppId& app_id) {
+  DCHECK(update_data_);
   WebApp* app = mutable_registrar_->GetAppByIdMutable(app_id);
   if (app)
-    apps_to_update_.insert(app);
+    update_data_->apps_to_update.insert(app);
 
   return app;
 }
 
-WebAppRegistryUpdate::AppsToUpdate WebAppRegistryUpdate::TakeAppsToUpdate() {
-  AppsToUpdate apps_to_update = std::move(apps_to_update_);
-  apps_to_update_.clear();
-  return apps_to_update;
+std::unique_ptr<RegistryUpdateData> WebAppRegistryUpdate::TakeUpdateData() {
+  return std::move(update_data_);
 }
 
 ScopedRegistryUpdate::ScopedRegistryUpdate(WebAppSyncBridge* sync_bridge)
diff --git a/chrome/browser/web_applications/web_app_registry_update.h b/chrome/browser/web_applications/web_app_registry_update.h
index 1bb1dd5..423e6c8 100644
--- a/chrome/browser/web_applications/web_app_registry_update.h
+++ b/chrome/browser/web_applications/web_app_registry_update.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_REGISTRY_UPDATE_H_
 
 #include <memory>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/containers/flat_set.h"
@@ -18,25 +19,45 @@
 class WebAppRegistrarMutable;
 class WebAppSyncBridge;
 
+// A raw registry update data.
+struct RegistryUpdateData {
+  RegistryUpdateData();
+  ~RegistryUpdateData();
+
+  using AppsToCreate = std::vector<std::unique_ptr<WebApp>>;
+  AppsToCreate apps_to_create;
+
+  using AppsToDelete = std::vector<AppId>;
+  AppsToDelete apps_to_delete;
+
+  using AppsToUpdate = base::flat_set<const WebApp*>;
+  AppsToUpdate apps_to_update;
+
+  bool IsEmpty() const;
+
+  DISALLOW_COPY_AND_ASSIGN(RegistryUpdateData);
+};
+
 // An explicit writable "view" for the registry. Any write operations must be
 // batched as a part of WebAppRegistryUpdate object. Effectively
 // WebAppRegistryUpdate is a part of WebAppSyncBridge class.
-//
-// TODO(loyso): Support creation and deletion of apps as a part of single
-// update (As in Database CRUD: Create/Update/Delete).
 class WebAppRegistryUpdate {
  public:
   explicit WebAppRegistryUpdate(WebAppRegistrarMutable* mutable_registrar);
   ~WebAppRegistryUpdate();
 
-  // Acquire a mutable app object to set new field values.
+  // Register a new app.
+  void CreateApp(std::unique_ptr<WebApp> web_app);
+  // Delete registered app.
+  void DeleteApp(const AppId& app_id);
+  // Acquire a mutable existing app to set new field values.
   WebApp* UpdateApp(const AppId& app_id);
 
-  using AppsToUpdate = base::flat_set<const WebApp*>;
-  AppsToUpdate TakeAppsToUpdate();
+  const RegistryUpdateData& update_data() const { return *update_data_; }
+  std::unique_ptr<RegistryUpdateData> TakeUpdateData();
 
  private:
-  AppsToUpdate apps_to_update_;
+  std::unique_ptr<RegistryUpdateData> update_data_;
   WebAppRegistrarMutable* mutable_registrar_;
 
   DISALLOW_COPY_AND_ASSIGN(WebAppRegistryUpdate);
diff --git a/chrome/browser/web_applications/web_app_sync_bridge.cc b/chrome/browser/web_applications/web_app_sync_bridge.cc
index 6033368..be0010a 100644
--- a/chrome/browser/web_applications/web_app_sync_bridge.cc
+++ b/chrome/browser/web_applications/web_app_sync_bridge.cc
@@ -8,9 +8,9 @@
 #include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
 #include "base/optional.h"
 #include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/web_app_database.h"
 #include "chrome/browser/web_applications/web_app_database_factory.h"
 #include "chrome/browser/web_applications/web_app_registry_update.h"
 #include "chrome/common/channel_info.h"
@@ -49,48 +49,13 @@
 
 WebAppSyncBridge::~WebAppSyncBridge() = default;
 
-void WebAppSyncBridge::RegisterApp(std::unique_ptr<WebApp> web_app) {
-  registrar_->CountMutation();
-
-  const auto app_id = web_app->app_id();
-  DCHECK(!app_id.empty());
-  DCHECK(!registrar_->GetAppById(app_id));
-
-  // TODO(loyso): Expose CompletionCallback as RegisterApp argument.
-  database_->WriteWebApps({web_app.get()}, base::DoNothing());
-
-  registrar_->registry().emplace(app_id, std::move(web_app));
-}
-
-std::unique_ptr<WebApp> WebAppSyncBridge::UnregisterApp(const AppId& app_id) {
-  registrar_->CountMutation();
-
-  DCHECK(!app_id.empty());
-
-  // TODO(loyso): Expose CompletionCallback as UnregisterApp argument.
-  database_->DeleteWebApps({app_id}, base::DoNothing());
-
-  auto it = registrar_->registry().find(app_id);
-  DCHECK(it != registrar_->registry().end());
-
-  auto web_app = std::move(it->second);
-  registrar_->registry().erase(it);
-  return web_app;
-}
-
-void WebAppSyncBridge::UnregisterAll() {
-  registrar_->CountMutation();
-
-  // TODO(loyso): Expose CompletionCallback as UnregisterAll argument.
-  database_->DeleteWebApps(registrar_->GetAppIds(), base::DoNothing());
-  registrar_->registry().clear();
-}
-
 std::unique_ptr<WebAppRegistryUpdate> WebAppSyncBridge::BeginUpdate() {
   DCHECK(!is_in_update_);
   is_in_update_ = true;
 
-  return base::WrapUnique(new WebAppRegistryUpdate(registrar_));
+  database_->BeginTransaction();
+
+  return std::make_unique<WebAppRegistryUpdate>(registrar_);
 }
 
 void WebAppSyncBridge::CommitUpdate(
@@ -99,28 +64,24 @@
   DCHECK(is_in_update_);
   is_in_update_ = false;
 
-  if (update == nullptr) {
+  if (update == nullptr || update->update_data().IsEmpty()) {
+    database_->CancelTransaction();
     std::move(callback).Run(/*success*/ true);
     return;
   }
 
-  WebAppRegistryUpdate::AppsToUpdate apps_to_update =
-      update->TakeAppsToUpdate();
+  CheckRegistryUpdateData(update->update_data());
 
-  if (apps_to_update.empty()) {
-    std::move(callback).Run(/*success*/ true);
-    return;
-  }
+  registrar_->CountMutation();
 
-#if DCHECK_IS_ON()
-  for (auto* app : apps_to_update)
-    DCHECK(registrar_->GetAppById(app->app_id()));
-#endif
+  std::unique_ptr<RegistryUpdateData> update_data = update->TakeUpdateData();
 
-  database_->WriteWebApps(
-      std::move(apps_to_update),
+  database_->CommitTransaction(
+      *update_data,
       base::BindOnce(&WebAppSyncBridge::OnDataWritten,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+
+  UpdateRegistrar(std::move(update_data));
 }
 
 void WebAppSyncBridge::Init(base::OnceClosure callback) {
@@ -141,6 +102,35 @@
   return this;
 }
 
+void WebAppSyncBridge::CheckRegistryUpdateData(
+    const RegistryUpdateData& update_data) const {
+#if DCHECK_IS_ON()
+  for (const std::unique_ptr<WebApp>& web_app : update_data.apps_to_create)
+    DCHECK(!registrar_->GetAppById(web_app->app_id()));
+
+  for (const AppId& app_id : update_data.apps_to_delete)
+    DCHECK(registrar_->GetAppById(app_id));
+
+  for (const WebApp* web_app : update_data.apps_to_update)
+    DCHECK(registrar_->GetAppById(web_app->app_id()));
+#endif
+}
+
+void WebAppSyncBridge::UpdateRegistrar(
+    std::unique_ptr<RegistryUpdateData> update_data) {
+  for (std::unique_ptr<WebApp>& web_app : update_data->apps_to_create) {
+    AppId app_id = web_app->app_id();
+    DCHECK(!registrar_->GetAppById(app_id));
+    registrar_->registry().emplace(std::move(app_id), std::move(web_app));
+  }
+
+  for (const AppId& app_id : update_data->apps_to_delete) {
+    auto it = registrar_->registry().find(app_id);
+    DCHECK(it != registrar_->registry().end());
+    registrar_->registry().erase(it);
+  }
+}
+
 void WebAppSyncBridge::OnDatabaseOpened(base::OnceClosure callback,
                                         Registry registry) {
   registrar_->InitRegistry(std::move(registry));
diff --git a/chrome/browser/web_applications/web_app_sync_bridge.h b/chrome/browser/web_applications/web_app_sync_bridge.h
index 88b384a7..ec4d60b 100644
--- a/chrome/browser/web_applications/web_app_sync_bridge.h
+++ b/chrome/browser/web_applications/web_app_sync_bridge.h
@@ -13,7 +13,6 @@
 #include "base/optional.h"
 #include "chrome/browser/web_applications/components/app_registry_controller.h"
 #include "chrome/browser/web_applications/web_app.h"
-#include "chrome/browser/web_applications/web_app_database.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "components/sync/model/model_type_sync_bridge.h"
 
@@ -26,7 +25,9 @@
 namespace web_app {
 
 class AbstractWebAppDatabaseFactory;
+class WebAppDatabase;
 class WebAppRegistryUpdate;
+struct RegistryUpdateData;
 
 // The sync bridge exclusively owns ModelTypeChangeProcessor and WebAppDatabase
 // (the storage).
@@ -44,12 +45,6 @@
       std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor);
   ~WebAppSyncBridge() override;
 
-  // TODO(loyso): Erase UnregisterAll. Move Register/Unregister app methods to
-  // BeginUpdate/CommitUpdate API.
-  void RegisterApp(std::unique_ptr<WebApp> web_app);
-  std::unique_ptr<WebApp> UnregisterApp(const AppId& app_id);
-  void UnregisterAll();
-
   using CommitCallback = base::OnceCallback<void(bool success)>;
   // This is the writable API for the registry. Any updates will be written to
   // LevelDb and sync service. There can be only 1 update at a time.
@@ -63,9 +58,11 @@
                              LaunchContainer launch_container) override;
   WebAppSyncBridge* AsWebAppSyncBridge() override;
 
-  WebAppDatabase& database_for_testing() { return *database_; }
-
  private:
+  void CheckRegistryUpdateData(const RegistryUpdateData& update_data) const;
+  // Update the in-memory model.
+  void UpdateRegistrar(std::unique_ptr<RegistryUpdateData> update_data);
+
   void OnDatabaseOpened(base::OnceClosure callback, Registry registry);
   void OnDataWritten(CommitCallback callback, bool success);
 
diff --git a/chrome/common/extensions/docs/templates/public/extensions/enterprise_hardwarePlatform.html b/chrome/common/extensions/docs/templates/public/extensions/enterprise_hardwarePlatform.html
new file mode 100644
index 0000000..25524de
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/public/extensions/enterprise_hardwarePlatform.html
@@ -0,0 +1 @@
+{{+partials.standard_extensions_api api:apis.extensions.enterprise_hardwarePlatform intro:intros.enterprise_hardwarePlatform/}}
diff --git a/chrome/common/search.mojom b/chrome/common/search.mojom
index bfad4719..070eaa36 100644
--- a/chrome/common/search.mojom
+++ b/chrome/common/search.mojom
@@ -63,6 +63,11 @@
   AutocompleteResultStatus status;
 };
 
+struct DeleteAutocompleteMatchResult {
+  bool success;
+  array<AutocompleteMatch> matches;
+};
+
 // Browser interface to support embedded search. Render frames connect to this
 // interface to query browser data, such as the most visited pages.
 // See http://dev.chromium.org/embeddedsearch
@@ -178,6 +183,11 @@
   QueryAutocomplete(mojo_base.mojom.String16 input) =>
         (AutocompleteResult result);
 
+  // Deletes the AutocompleteMatch in the current results by |line| number if
+  // it is deletable. When a match is deleted, |matches| is populated with the
+  // updated result after deletion. Otherwise, |matches| is empty.
+  DeleteAutocompleteMatch(uint8 line) => (DeleteAutocompleteMatchResult result);
+
   // Cancels the current autocomplete query. Clears the result set if
   // |clear_result| is true.
   StopAutocomplete(bool clear_result);
diff --git a/chrome/renderer/autofill/form_autofill_browsertest.cc b/chrome/renderer/autofill/form_autofill_browsertest.cc
index 5f01b20..1d92455 100644
--- a/chrome/renderer/autofill/form_autofill_browsertest.cc
+++ b/chrome/renderer/autofill/form_autofill_browsertest.cc
@@ -2354,12 +2354,6 @@
     EXPECT_FALSE(phone.IsAutofilled());
   }
 
-  static void FillFormIncludingNonFocusableElementsWrapper(
-      const FormData& form,
-      const WebFormControlElement& element) {
-    FillFormIncludingNonFocusableElements(form, element.Form());
-  }
-
   static WebString GetValueWrapper(WebFormControlElement element) {
     if (element.FormControlType() == "textarea")
       return element.To<WebFormControlElement>().Value();
@@ -3646,92 +3640,6 @@
   TestFillForm(html.c_str(), true, nullptr);
 }
 
-TEST_F(FormAutofillTest, FillFormIncludingNonFocusableElements) {
-  static const AutofillFieldCase field_cases[] = {
-      // fields: form_control_type, name, initial_value, autocomplete_attribute,
-      //         should_be_autofilled, autofill_value, expected_value
-
-      // Regular empty fields (firstname & lastname) should be autofilled.
-      {"text",
-       "firstname",
-       "",
-       "",
-       true,
-       "filled firstname",
-       "filled firstname"},
-      {"text", "lastname", "", "", true, "filled lastname", "filled lastname"},
-      // hidden fields should not be extracted to form_data.
-      // Non empty fields should be overriden.
-      {"text",
-       "notempty",
-       "Hi",
-       "",
-       true,
-       "filled notempty",
-       "filled notempty"},
-      {"text",
-       "noautocomplete",
-       "",
-       "off",
-       true,
-       "filled noautocomplete",
-       "filled noautocomplete"},
-      // Disabled fields should not be autofilled.
-      {"text", "notenabled", "", "", false, "filled notenabled", ""},
-      // Readonly fields should not be autofilled.
-      {"text", "readonly", "", "", false, "filled readonly", ""},
-      // Fields with "visibility: hidden" should also be autofilled.
-      {"text",
-       "invisible",
-       "",
-       "",
-       true,
-       "filled invisible",
-       "filled invisible"},
-      // Fields with "display:none" should also be autofilled.
-      {"text",
-       "displaynone",
-       "",
-       "",
-       true,
-       "filled displaynone",
-       "filled displaynone"},
-      // Regular <input type="month"> should be autofilled.
-      {"month", "month", "", "", true, "2017-11", "2017-11"},
-      // Non-empty <input type="month"> should be overridden.
-      {"month", "month-nonempty", "2011-12", "", true, "2017-11", "2017-11"},
-      // Regular select fields should be autofilled.
-      {"select-one", "select", "", "", true, "TX", "TX"},
-      // Select fields should be autofilled even if they already have a
-      // non-empty value.
-      {"select-one", "select-nonempty", "CA", "", true, "TX", "TX"},
-      // Select fields should not be autofilled if no new value is passed from
-      // autofill profile. The existing value should not be overriden.
-      {"select-one", "select-unchanged", "CA", "", false, "CA", "CA"},
-      // Select fields that are not focusable should always be filled.
-      {"select-one", "select-displaynone", "CA", "", true, "CA", "CA"},
-      // Regular textarea elements should be autofilled.
-      {"textarea",
-       "textarea",
-       "",
-       "",
-       true,
-       "some multi-\nline value",
-       "some multi-\nline value"},
-      // Nonempty textarea elements should be overridden.
-      {"textarea",
-       "textarea-nonempty",
-       "Go\naway!",
-       "",
-       true,
-       "some multi-\nline value",
-       "some multi-\nline value"},
-  };
-  TestFormFillFunctions(
-      kFormHtml, false, nullptr, field_cases, base::size(field_cases),
-      &FillFormIncludingNonFocusableElementsWrapper, &GetValueWrapper);
-}
-
 TEST_F(FormAutofillTest, PreviewForm) {
   TestPreviewForm(kFormHtml, false, nullptr);
 }
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index cd1b90d..cde3851 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -70,10 +70,6 @@
 // The name of the username/password element in the form.
 const char kUsernameName[] = "username";
 const char kPasswordName[] = "password";
-const char kDisplayName[] = "display-name";
-const char kCreditCardOwnerName[] = "creditcardowner";
-const char kCreditCardNumberName[] = "creditcardnumber";
-const char kCreditCardVerificationName[] = "cvc";
 const char kSearchField[] = "search";
 const char kSocialMediaTextArea[] = "new_chirp";
 
@@ -219,15 +215,6 @@
     "form.dispatchEvent(event);"
     "console.log('clicked!');";
 
-const char kFormHTMLWithTwoTextFields[] =
-    "<FORM name='LoginTestForm' id='LoginTestForm' "
-    "action='http://www.bidule.com'>"
-    "  <INPUT type='text' id='display-name'/>"
-    "  <INPUT type='text' id='username'/>"
-    "  <INPUT type='password' id='password'/>"
-    "  <INPUT type='submit' value='Login'/>"
-    "</FORM>";
-
 const char kPasswordChangeFormHTML[] =
     "<FORM name='ChangeWithUsernameForm' action='http://www.bidule.com'>"
     "  <INPUT type='text' id='username'/>"
@@ -1894,7 +1881,7 @@
 // PasswordAutofillAgent can still remember the username and the password
 // typed by the user.
 TEST_F(PasswordAutofillAgentTest,
-       RememberLastNonEmptyUsernameAndPasswordOnSubmit_ScriptCleared) {
+       DISABLED_RememberLastNonEmptyUsernameAndPasswordOnSubmit_ScriptCleared) {
   LoadHTML(kSignupFormHTML);
   WebInputElement username_element = GetInputElementByID("random_info");
   ASSERT_FALSE(username_element.IsNull());
@@ -1957,7 +1944,7 @@
 // Similar to RememberLastNonEmptyPasswordOnSubmit_ScriptCleared, but uses the
 // new password instead of the current password.
 TEST_F(PasswordAutofillAgentTest,
-       RememberLastNonEmptyUsernameAndPasswordOnSubmit_New) {
+       DISABLED_RememberLastNonEmptyUsernameAndPasswordOnSubmit_New) {
   const char kNewPasswordFormHTML[] =
       "<FORM name='LoginTestForm' action='http://www.bidule.com'>"
       "  <INPUT type='text' id='username' autocomplete='username'/>"
@@ -2314,68 +2301,6 @@
                                               std::string(), false);
 }
 
-// Test that the last plain text field before a password field is chosen as a
-// username, in a form with 2 plain text fields without username predictions.
-TEST_F(PasswordAutofillAgentTest, FindingUsernameWithoutAutofillPredictions) {
-  LoadHTML(kFormHTMLWithTwoTextFields);
-  UpdateUsernameAndPasswordElements();
-  WebInputElement display_name_element = GetInputElementByID(kDisplayName);
-  SimulateUsernameTyping("temp");
-  SimulateUserInputChangeForElement(&display_name_element, "User123");
-  SimulatePasswordTyping("random");
-
-  SaveAndSubmitForm();
-
-  // Observe that the PasswordAutofillAgent identifies the second field as
-  // username.
-  ExpectFormSubmittedWithUsernameAndPasswords("temp", "random", "");
-}
-
-// Tests that field predictions are followed when identifying the username
-// and password in a password form with two plain text fields.
-TEST_F(PasswordAutofillAgentTest, FindingFieldsWithAutofillPredictions) {
-  LoadHTML(kFormHTMLWithTwoTextFields);
-  UpdateUsernameAndPasswordElements();
-  WebInputElement display_name_element = GetInputElementByID(kDisplayName);
-  SimulateUsernameTyping("temp");
-  SimulateUserInputChangeForElement(&display_name_element, "User123");
-  SimulatePasswordTyping("random");
-  // Find FormData for visible password form.
-  WebFormElement form_element = username_element_.Form();
-  FormData form_data;
-  ASSERT_TRUE(WebFormElementToFormData(
-      form_element, blink::WebFormControlElement(), nullptr,
-      form_util::EXTRACT_NONE, &form_data, nullptr));
-  // Simulate Autofill predictions: the first field is username, the third
-  // one is password.
-  autofill::FormsPredictionsMap predictions;
-  predictions[form_data][form_data.fields[0]] =
-      PasswordFormFieldPredictionType::kUsername;
-  predictions[form_data][form_data.fields[2]] =
-      PasswordFormFieldPredictionType::kNewPassword;
-  password_autofill_agent_->AutofillUsernameAndPasswordDataReceived(
-      predictions);
-
-  // The predictions should still match even if the form changes, as long
-  // as the particular elements don't change.
-  std::string add_field_to_form =
-      "var form = document.getElementById('LoginTestForm');"
-      "var new_input = document.createElement('input');"
-      "new_input.setAttribute('type', 'text');"
-      "new_input.setAttribute('id', 'other_field');"
-      "form.appendChild(new_input);";
-  ExecuteJavaScriptForTests(add_field_to_form.c_str());
-
-  SaveAndSubmitForm();
-
-  // Observe that the PasswordAutofillAgent identifies the first field as
-  // username.
-  // TODO(msramek): We should also test that adding another password field
-  // won't override the password field prediction either. However, the password
-  // field predictions are not taken into account yet.
-  ExpectFormSubmittedWithUsernameAndPasswords("User123", "random", "");
-}
-
 // The user types in a username and a password. Then JavaScript changes password
 // field to readonly state before submit. PasswordAutofillAgent can correctly
 // process readonly password field. This test models behaviour of gmail.com.
@@ -2569,39 +2494,6 @@
   CheckSuggestions("", false);
 }
 
-// Tests that NOT_PASSWORD field predictions are followed so that no password
-// form is submitted.
-TEST_F(PasswordAutofillAgentTest, IgnoreNotPasswordFields) {
-  LoadHTML(kCreditCardFormHTML);
-  WebInputElement credit_card_owner_element =
-      GetInputElementByID(kCreditCardOwnerName);
-  WebInputElement credit_card_number_element =
-      GetInputElementByID(kCreditCardNumberName);
-  WebInputElement credit_card_verification_element =
-      GetInputElementByID(kCreditCardVerificationName);
-  SimulateUserInputChangeForElement(&credit_card_owner_element, "JohnSmith");
-  SimulateUserInputChangeForElement(&credit_card_number_element,
-                                    "1234123412341234");
-  SimulateUserInputChangeForElement(&credit_card_verification_element, "123");
-  // Find FormData for visible form.
-  WebFormElement form_element = credit_card_number_element.Form();
-  FormData form_data;
-  ASSERT_TRUE(WebFormElementToFormData(
-      form_element, blink::WebFormControlElement(), nullptr,
-      form_util::EXTRACT_NONE, &form_data, nullptr));
-  // Simulate Autofill predictions: the third field is not a password.
-  autofill::FormsPredictionsMap predictions;
-  predictions[form_data][form_data.fields[2]] =
-      PasswordFormFieldPredictionType::kNotPassword;
-  password_autofill_agent_->AutofillUsernameAndPasswordDataReceived(
-      predictions);
-
-  SaveAndSubmitForm(form_element);
-
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(fake_driver_.called_password_form_submitted_only_for_fallback());
-}
-
 // Tests that only the password field is autocompleted when the browser sends
 // back data with only one credentials and empty username.
 TEST_F(PasswordAutofillAgentTest, NotAutofillNoUsername) {
diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc
index 6453f04..65c24c67 100644
--- a/chrome/renderer/searchbox/searchbox.cc
+++ b/chrome/renderer/searchbox/searchbox.cc
@@ -438,6 +438,12 @@
                             weak_ptr_factory_.GetWeakPtr()));
 }
 
+void SearchBox::DeleteAutocompleteMatch(uint8_t line) {
+  embedded_search_service_->DeleteAutocompleteMatch(
+      line, base::BindOnce(&SearchBox::OnDeleteAutocompleteMatch,
+                           base::Unretained(this)));
+}
+
 void SearchBox::StopAutocomplete(bool clear_result) {
   embedded_search_service_->StopAutocomplete(clear_result);
 }
@@ -454,6 +460,14 @@
   }
 }
 
+void SearchBox::OnDeleteAutocompleteMatch(
+    chrome::mojom::DeleteAutocompleteMatchResultPtr result) {
+  if (can_run_js_in_renderframe_) {
+    SearchBoxExtension::DispatchDeleteAutocompleteMatchResult(
+        render_frame()->GetWebFrame(), std::move(result));
+  }
+}
+
 void SearchBox::SetPageSequenceNumber(int page_seq_no) {
   page_seq_no_ = page_seq_no;
 }
diff --git a/chrome/renderer/searchbox/searchbox.h b/chrome/renderer/searchbox/searchbox.h
index ab75216..c7755aa 100644
--- a/chrome/renderer/searchbox/searchbox.h
+++ b/chrome/renderer/searchbox/searchbox.h
@@ -193,6 +193,9 @@
   // search term. Handled by |QueryAutocompleteResult|.
   void QueryAutocomplete(const base::string16& input);
 
+  // Deletes |AutocompleteMatch| by index of the result.
+  void DeleteAutocompleteMatch(uint8_t line);
+
   // Cancels the current autocomplete query. Clears the result set if
   // |clear_result| is true.
   void StopAutocomplete(bool clear_result);
@@ -230,6 +233,11 @@
   // Asynchronous callback for autocomplete query results. Sends to renderer.
   void QueryAutocompleteResult(chrome::mojom::AutocompleteResultPtr result);
 
+  // Asynchronous callback for results of attempting to delete an autocomplete
+  // result.
+  void OnDeleteAutocompleteMatch(
+      chrome::mojom::DeleteAutocompleteMatchResultPtr result);
+
   // The connection to the EmbeddedSearch service in the browser process.
   chrome::mojom::EmbeddedSearchAssociatedPtr embedded_search_service_;
   mojo::AssociatedBinding<chrome::mojom::EmbeddedSearchClient> binding_;
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index c293b929..95df2fe 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -400,6 +400,43 @@
   return SearchBox::Get(main_frame);
 }
 
+base::Value CreateAutocompleteMatches(
+    const std::vector<chrome::mojom::AutocompleteMatchPtr>& matches) {
+  base::Value list(base::Value::Type::LIST);
+  for (const chrome::mojom::AutocompleteMatchPtr& match : matches) {
+    base::Value dict(base::Value::Type::DICTIONARY);
+    dict.SetBoolKey("allowedToBeDefaultMatch",
+                    match->allowed_to_be_default_match);
+    dict.SetStringKey("contents", match->contents);
+    base::Value contents_class(base::Value::Type::LIST);
+    for (const auto& classification : match->contents_class) {
+      base::Value entry(base::Value::Type::DICTIONARY);
+      entry.SetIntKey("offset", classification->offset);
+      entry.SetIntKey("style", classification->style);
+      contents_class.Append(std::move(entry));
+    }
+    dict.SetKey("contentsClass", std::move(contents_class));
+    dict.SetStringKey("description", match->description);
+    base::Value description_class(base::Value::Type::LIST);
+    for (const auto& classification : match->description_class) {
+      base::Value entry(base::Value::Type::DICTIONARY);
+      entry.SetIntKey("offset", classification->offset);
+      entry.SetIntKey("style", classification->style);
+      description_class.Append(std::move(entry));
+    }
+    dict.SetKey("descriptionClass", std::move(description_class));
+    dict.SetStringKey("destinationUrl", match->destination_url);
+    dict.SetStringKey("inlineAutocompletion", match->inline_autocompletion);
+    dict.SetBoolKey("isSearchType", match->is_search_type);
+    dict.SetStringKey("fillIntoEdit", match->fill_into_edit);
+    dict.SetBoolKey("swapContentsAndDescription",
+                    match->swap_contents_and_description);
+    dict.SetStringKey("type", match->type);
+    list.Append(std::move(dict));
+  }
+  return list;
+}
+
 static const char kDispatchFocusChangedScript[] =
     "if (window.chrome &&"
     "    window.chrome.embeddedSearch &&"
@@ -444,6 +481,17 @@
     "  true;"
     "}";
 
+static const char kDispatchDeleteAutocompleteMatchResult[] =
+    "if (window.chrome &&"
+    "    window.chrome.embeddedSearch &&"
+    "    window.chrome.embeddedSearch.searchBox &&"
+    "    window.chrome.embeddedSearch.searchBox.ondeleteautocompletematch &&"
+    "    typeof window.chrome.embeddedSearch.searchBox"
+    "        .ondeleteautocompletematch === 'function') {"
+    "  window.chrome.embeddedSearch.searchBox.ondeleteautocompletematch(%s);"
+    "  true;"
+    "}";
+
 static const char kDispatchDeleteCustomLinkResult[] =
     "if (window.chrome &&"
     "    window.chrome.embeddedSearch &&"
@@ -543,6 +591,7 @@
   static bool IsKeyCaptureEnabled();
 
   // Handlers for JS functions.
+  static void DeleteAutocompleteMatch(int line);
   static void Paste(const std::string& text);
   static void QueryAutocomplete(const base::string16& input);
   static void StopAutocomplete(bool clear_result);
@@ -565,6 +614,8 @@
       .SetProperty("isFocused", &SearchBoxBindings::IsFocused)
       .SetProperty("isKeyCaptureEnabled",
                    &SearchBoxBindings::IsKeyCaptureEnabled)
+      .SetMethod("deleteAutocompleteMatch",
+                 &SearchBoxBindings::DeleteAutocompleteMatch)
       .SetMethod("paste", &SearchBoxBindings::Paste)
       .SetMethod("queryAutocomplete", &SearchBoxBindings::QueryAutocomplete)
       .SetMethod("stopAutocomplete", &SearchBoxBindings::StopAutocomplete)
@@ -591,6 +642,16 @@
 }
 
 // static
+void SearchBoxBindings::DeleteAutocompleteMatch(int line) {
+  DCHECK_GE(line, 0);
+  DCHECK_LE(line, 255);
+  SearchBox* search_box = GetSearchBoxForCurrentContext();
+  if (!search_box)
+    return;
+  search_box->DeleteAutocompleteMatch(line);
+}
+
+// static
 void SearchBoxBindings::Paste(const std::string& text) {
   SearchBox* search_box = GetSearchBoxForCurrentContext();
   if (!search_box)
@@ -1353,40 +1414,7 @@
   base::Value dict(base::Value::Type::DICTIONARY);
   dict.SetStringKey("input", result->input);
   dict.SetDoubleKey("status", static_cast<double>(result->status));
-
-  base::Value list(base::Value::Type::LIST);
-  for (const chrome::mojom::AutocompleteMatchPtr& match : result->matches) {
-    base::Value dict(base::Value::Type::DICTIONARY);
-    dict.SetBoolKey("allowedToBeDefaultMatch",
-                    match->allowed_to_be_default_match);
-    dict.SetStringKey("contents", match->contents);
-    base::Value contents_class(base::Value::Type::LIST);
-    for (const auto& classification : match->contents_class) {
-      base::Value entry(base::Value::Type::DICTIONARY);
-      entry.SetIntKey("offset", classification->offset);
-      entry.SetIntKey("style", classification->style);
-      contents_class.Append(std::move(entry));
-    }
-    dict.SetKey("contentsClass", std::move(contents_class));
-    dict.SetStringKey("description", match->description);
-    base::Value description_class(base::Value::Type::LIST);
-    for (const auto& classification : match->description_class) {
-      base::Value entry(base::Value::Type::DICTIONARY);
-      entry.SetIntKey("offset", classification->offset);
-      entry.SetIntKey("style", classification->style);
-      description_class.Append(std::move(entry));
-    }
-    dict.SetKey("descriptionClass", std::move(description_class));
-    dict.SetStringKey("destinationUrl", match->destination_url);
-    dict.SetStringKey("inlineAutocompletion", match->inline_autocompletion);
-    dict.SetBoolKey("isSearchType", match->is_search_type);
-    dict.SetStringKey("fillIntoEdit", match->fill_into_edit);
-    dict.SetBoolKey("swapContentsAndDescription",
-                    match->swap_contents_and_description);
-    dict.SetStringKey("type", match->type);
-    list.Append(std::move(dict));
-  }
-  dict.SetKey("matches", std::move(list));
+  dict.SetKey("matches", CreateAutocompleteMatches(result->matches));
 
   std::string json;
   base::JSONWriter::Write(dict, &json);
@@ -1394,6 +1422,19 @@
                       kDispatchQueryAutocompleteResult, json.c_str())));
 }
 
+void SearchBoxExtension::DispatchDeleteAutocompleteMatchResult(
+    blink::WebLocalFrame* frame,
+    chrome::mojom::DeleteAutocompleteMatchResultPtr result) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetBoolKey("success", result->success);
+  dict.SetKey("matches", CreateAutocompleteMatches(result->matches));
+
+  std::string json;
+  base::JSONWriter::Write(dict, &json);
+  Dispatch(frame, blink::WebString::FromUTF8(base::StringPrintf(
+                      kDispatchDeleteAutocompleteMatchResult, json.c_str())));
+}
+
 // static
 void SearchBoxExtension::DispatchInputCancel(blink::WebLocalFrame* frame) {
   Dispatch(frame, kDispatchInputCancelScript);
diff --git a/chrome/renderer/searchbox/searchbox_extension.h b/chrome/renderer/searchbox/searchbox_extension.h
index a5f56f5..5d2052e 100644
--- a/chrome/renderer/searchbox/searchbox_extension.h
+++ b/chrome/renderer/searchbox/searchbox_extension.h
@@ -40,6 +40,9 @@
   static void DispatchQueryAutocompleteResult(
       blink::WebLocalFrame* frame,
       chrome::mojom::AutocompleteResultPtr result);
+  static void DispatchDeleteAutocompleteMatchResult(
+      blink::WebLocalFrame* frame,
+      chrome::mojom::DeleteAutocompleteMatchResultPtr result);
   static void DispatchInputCancel(blink::WebLocalFrame* frame);
   static void DispatchInputStart(blink::WebLocalFrame* frame);
   static void DispatchKeyCaptureChange(blink::WebLocalFrame* frame);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 49c70c3..f100f41 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2045,6 +2045,8 @@
         "../browser/chromeos/extensions/login_screen/login_state/login_state_apitest.cc",
         "../browser/chromeos/extensions/login_screen/login_state/session_state_changed_event_dispatcher_apitest.cc",
         "../browser/chromeos/extensions/login_screen/storage_apitest.cc",
+        "../browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher_apitest.cc",
+        "../browser/chromeos/extensions/printing_metrics/printing_metrics_apitest.cc",
         "../browser/chromeos/extensions/users_private/users_private_apitest.cc",
         "../browser/chromeos/extensions/wallpaper_apitest.cc",
         "../browser/chromeos/extensions/wallpaper_manager_browsertest.cc",
@@ -2249,6 +2251,10 @@
         "../browser/chromeos/policy/user_policy_test_helper.h",
         "../browser/chromeos/policy/variations_service_policy_browsertest.cc",
         "../browser/chromeos/preferences_chromeos_browsertest.cc",
+        "../browser/chromeos/printing/history/test_print_job_history_service_observer.cc",
+        "../browser/chromeos/printing/history/test_print_job_history_service_observer.h",
+        "../browser/chromeos/printing/test_cups_print_job_manager.cc",
+        "../browser/chromeos/printing/test_cups_print_job_manager.h",
         "../browser/chromeos/profiles/profile_helper_browsertest.cc",
         "../browser/chromeos/shutdown_policy_browsertest.cc",
         "../browser/chromeos/startup_settings_cache_browsertest.cc",
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl.cc b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
index 4d008c2c..092c250 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl.cc
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
@@ -314,6 +314,9 @@
   command.SetInteger("id", command_id);
   command.SetString("method", method);
   command.SetKey("params", params.Clone());
+  if (parent_ != nullptr) {
+    command.SetString("sessionId", session_id_);
+  }
   std::string message = SerializeValue(&command);
   if (IsVLogOn(1)) {
     // Note: ChromeDriver log-replay depends on the format of this logging.
@@ -321,16 +324,9 @@
     VLOG(1) << "DevTools WebSocket Command: " << method << " (id=" << command_id
             << ") " << id_ << " " << FormatValueForDisplay(params);
   }
-  if (parent_ != nullptr) {
-    base::DictionaryValue params2;
-    params2.SetString("sessionId", session_id_);
-    params2.SetString("message", message);
-    Status status =
-        parent_->SendCommandInternal("Target.sendMessageToTarget", params2,
-                                     nullptr, true, false, 0, timeout);
-    if (status.IsError())
-      return status;
-  } else if (!socket_->Send(message)) {
+  SyncWebSocket* socket =
+      (parent_ != nullptr) ? parent_->socket_.get() : socket_.get();
+  if (!socket->Send(message)) {
     return Status(kDisconnected, "unable to send message to renderer");
   }
 
@@ -357,8 +353,9 @@
       }
       CHECK_EQ(response_info->state, kReceived);
       internal::InspectorCommandResponse& response = response_info->response;
-      if (!response.result)
+      if (!response.result) {
         return internal::ParseInspectorError(response.error);
+      }
       *result = std::move(response.result);
     }
   } else {
@@ -426,18 +423,32 @@
 
 Status DevToolsClientImpl::HandleMessage(int expected_id,
                                          const std::string& message) {
+  std::string session_id;
   internal::InspectorMessageType type;
   internal::InspectorEvent event;
   internal::InspectorCommandResponse response;
-  if (!parser_func_.Run(message, expected_id, &type, &event, &response)) {
+  if (!parser_func_.Run(message, expected_id, &session_id, &type, &event,
+                        &response)) {
     LOG(ERROR) << "Bad inspector message: " << message;
     return Status(kUnknownError, "bad inspector message: " + message);
   }
-
-  if (type == internal::kEventMessageType)
-    return ProcessEvent(event);
+  DevToolsClientImpl* client = this;
+  if (session_id != session_id_) {
+    auto it = children_.find(session_id);
+    if (it == children_.end()) {
+      // ChromeDriver only cares about iframe targets, but uses
+      // Target.setAutoAttach in FrameTracker. If we don't know about this
+      // sessionId, then it must be of a different target type and should be
+      // ignored.
+      return Status(kOk);
+    }
+    client = it->second;
+  }
+  if (type == internal::kEventMessageType) {
+    return client->ProcessEvent(event);
+  }
   CHECK_EQ(type, internal::kCommandResponseMessageType);
-  return ProcessCommandResponse(response);
+  return client->ProcessCommandResponse(response);
 }
 
 Status DevToolsClientImpl::ProcessEvent(const internal::InspectorEvent& event) {
@@ -483,27 +494,6 @@
     if (enable_status.IsError())
       return status;
   }
-  if (event.method == "Target.receivedMessageFromTarget") {
-    std::string session_id;
-    if (!event.params->GetString("sessionId", &session_id))
-      return Status(
-          kUnknownError,
-          "missing sessionId in Target.receivedMessageFromTarget event");
-    if (children_.count(session_id) == 0)
-      // ChromeDriver only cares about iframe targets. If we don't know about
-      // this sessionId, then it must be of a different target type and should
-      // be ignored.
-      return Status(kOk);
-    DevToolsClientImpl* child = children_[session_id];
-    std::string message;
-    if (!event.params->GetString("message", &message))
-      return Status(
-          kUnknownError,
-          "missing message in Target.receivedMessageFromTarget event");
-
-    WebViewImplHolder childHolder(child->owner_);
-    return child->HandleMessage(-1, message);
-  }
   return Status(kOk);
 }
 
@@ -592,12 +582,12 @@
 
 namespace internal {
 
-bool ParseInspectorMessage(
-    const std::string& message,
-    int expected_id,
-    InspectorMessageType* type,
-    InspectorEvent* event,
-    InspectorCommandResponse* command_response) {
+bool ParseInspectorMessage(const std::string& message,
+                           int expected_id,
+                           std::string* session_id,
+                           InspectorMessageType* type,
+                           InspectorEvent* event,
+                           InspectorCommandResponse* command_response) {
   // We want to allow invalid characters in case they are valid ECMAScript
   // strings. For example, webplatform tests use this to check string handling
   std::unique_ptr<base::Value> message_value = base::JSONReader::ReadDeprecated(
@@ -605,7 +595,9 @@
   base::DictionaryValue* message_dict;
   if (!message_value || !message_value->GetAsDictionary(&message_dict))
     return false;
-
+  session_id->clear();
+  if (message_dict->HasKey("sessionId"))
+    message_dict->GetString("sessionId", session_id);
   int id;
   if (!message_dict->HasKey("id")) {
     std::string method;
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl.h b/chrome/test/chromedriver/chrome/devtools_client_impl.h
index cf3d7f0..4234e3a 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl.h
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl.h
@@ -68,12 +68,13 @@
 
   DevToolsClientImpl(DevToolsClientImpl* parent, const std::string& session_id);
 
-  typedef base::Callback<bool(
-      const std::string&,
-      int,
-      internal::InspectorMessageType*,
-      internal::InspectorEvent*,
-      internal::InspectorCommandResponse*)> ParserFunc;
+  typedef base::Callback<bool(const std::string&,
+                              int,
+                              std::string*,
+                              internal::InspectorMessageType*,
+                              internal::InspectorEvent*,
+                              internal::InspectorCommandResponse*)>
+      ParserFunc;
   DevToolsClientImpl(const SyncWebSocketFactory& factory,
                      const std::string& url,
                      const std::string& id,
@@ -170,6 +171,8 @@
   std::map<std::string, DevToolsClientImpl*> children_;
   bool crashed_;
   bool detached_;
+  // For the top-level session, this is the target id.
+  // For child sessions, it's the session id.
   const std::string id_;
   FrontendCloserFunc frontend_closer_func_;
   ParserFunc parser_func_;
@@ -180,7 +183,7 @@
   std::list<DevToolsEventListener*> unnotified_cmd_response_listeners_;
   scoped_refptr<ResponseInfo> unnotified_cmd_response_info_;
   std::map<int, scoped_refptr<ResponseInfo>> response_info_map_;
-  int next_id_;
+  int next_id_;  // The id identifying a particular request.
   int stack_count_;
 
   DISALLOW_COPY_AND_ASSIGN(DevToolsClientImpl);
@@ -188,12 +191,12 @@
 
 namespace internal {
 
-bool ParseInspectorMessage(
-    const std::string& message,
-    int expected_id,
-    InspectorMessageType* type,
-    InspectorEvent* event,
-    InspectorCommandResponse* command_response);
+bool ParseInspectorMessage(const std::string& message,
+                           int expected_id,
+                           std::string* session_id,
+                           InspectorMessageType* type,
+                           InspectorEvent* event,
+                           InspectorCommandResponse* command_response);
 
 Status ParseInspectorError(const std::string& error_json);
 
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc b/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc
index 435271b..9f13a23 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc
@@ -273,49 +273,53 @@
   bool connected_;
 };
 
-bool ReturnCommand(
-    const std::string& message,
-    int expected_id,
-    internal::InspectorMessageType* type,
-    internal::InspectorEvent* event,
-    internal::InspectorCommandResponse* command_response) {
+bool ReturnCommand(const std::string& message,
+                   int expected_id,
+                   std::string* session_id,
+                   internal::InspectorMessageType* type,
+                   internal::InspectorEvent* event,
+                   internal::InspectorCommandResponse* command_response) {
   *type = internal::kCommandResponseMessageType;
+  session_id->clear();
   command_response->id = expected_id;
   command_response->result.reset(new base::DictionaryValue());
   return true;
 }
 
-bool ReturnBadResponse(
-    const std::string& message,
-    int expected_id,
-    internal::InspectorMessageType* type,
-    internal::InspectorEvent* event,
-    internal::InspectorCommandResponse* command_response) {
+bool ReturnBadResponse(const std::string& message,
+                       int expected_id,
+                       std::string* session_id,
+                       internal::InspectorMessageType* type,
+                       internal::InspectorEvent* event,
+                       internal::InspectorCommandResponse* command_response) {
   *type = internal::kCommandResponseMessageType;
+  session_id->clear();
   command_response->id = expected_id;
   command_response->result.reset(new base::DictionaryValue());
   return false;
 }
 
-bool ReturnCommandBadId(
-    const std::string& message,
-    int expected_id,
-    internal::InspectorMessageType* type,
-    internal::InspectorEvent* event,
-    internal::InspectorCommandResponse* command_response) {
+bool ReturnCommandBadId(const std::string& message,
+                        int expected_id,
+                        std::string* session_id,
+                        internal::InspectorMessageType* type,
+                        internal::InspectorEvent* event,
+                        internal::InspectorCommandResponse* command_response) {
   *type = internal::kCommandResponseMessageType;
+  session_id->clear();
   command_response->id = expected_id + 100;
   command_response->result.reset(new base::DictionaryValue());
   return true;
 }
 
-bool ReturnCommandError(
-    const std::string& message,
-    int expected_id,
-    internal::InspectorMessageType* type,
-    internal::InspectorEvent* event,
-    internal::InspectorCommandResponse* command_response) {
+bool ReturnCommandError(const std::string& message,
+                        int expected_id,
+                        std::string* session_id,
+                        internal::InspectorMessageType* type,
+                        internal::InspectorEvent* event,
+                        internal::InspectorCommandResponse* command_response) {
   *type = internal::kCommandResponseMessageType;
+  session_id->clear();
   command_response->id = expected_id;
   command_response->error = "err";
   return true;
@@ -345,9 +349,11 @@
     bool* first,
     const std::string& message,
     int expected_id,
+    std::string* session_id,
     internal::InspectorMessageType* type,
     internal::InspectorEvent* event,
     internal::InspectorCommandResponse* command_response) {
+  session_id->clear();
   if (*first) {
     *type = internal::kEventMessageType;
     event->method = "method";
@@ -364,12 +370,12 @@
   return true;
 }
 
-bool ReturnEvent(
-    const std::string& message,
-    int expected_id,
-    internal::InspectorMessageType* type,
-    internal::InspectorEvent* event,
-    internal::InspectorCommandResponse* command_response) {
+bool ReturnEvent(const std::string& message,
+                 int expected_id,
+                 std::string* session_id,
+                 internal::InspectorMessageType* type,
+                 internal::InspectorEvent* event,
+                 internal::InspectorCommandResponse* command_response) {
   *type = internal::kEventMessageType;
   event->method = "method";
   event->params.reset(new base::DictionaryValue());
@@ -382,6 +388,7 @@
     DevToolsClient* client,
     const std::string& message,
     int expected_id,
+    std::string* session_id,
     internal::InspectorMessageType* type,
     internal::InspectorEvent* event,
     internal::InspectorCommandResponse* command_response) {
@@ -411,12 +418,12 @@
   return true;
 }
 
-bool ReturnError(
-    const std::string& message,
-    int expected_id,
-    internal::InspectorMessageType* type,
-    internal::InspectorEvent* event,
-    internal::InspectorCommandResponse* command_response) {
+bool ReturnError(const std::string& message,
+                 int expected_id,
+                 std::string* session_id,
+                 internal::InspectorMessageType* type,
+                 internal::InspectorEvent* event,
+                 internal::InspectorCommandResponse* command_response) {
   return false;
 }
 
@@ -499,61 +506,84 @@
   internal::InspectorMessageType type;
   internal::InspectorEvent event;
   internal::InspectorCommandResponse response;
-  ASSERT_FALSE(internal::ParseInspectorMessage(
-      "hi", 0, &type, &event, &response));
+  std::string session_id;
+  ASSERT_FALSE(internal::ParseInspectorMessage("hi", 0, &session_id, &type,
+                                               &event, &response));
 }
 
 TEST(ParseInspectorMessage, NeitherCommandNorEvent) {
   internal::InspectorMessageType type;
   internal::InspectorEvent event;
   internal::InspectorCommandResponse response;
-  ASSERT_FALSE(internal::ParseInspectorMessage(
-      "{}", 0, &type, &event, &response));
+  std::string session_id;
+  ASSERT_FALSE(internal::ParseInspectorMessage("{}", 0, &session_id, &type,
+                                               &event, &response));
 }
 
 TEST(ParseInspectorMessage, EventNoParams) {
   internal::InspectorMessageType type;
   internal::InspectorEvent event;
   internal::InspectorCommandResponse response;
+  std::string session_id;
   ASSERT_TRUE(internal::ParseInspectorMessage(
-      "{\"method\":\"method\"}", 0, &type, &event, &response));
+      "{\"method\":\"method\"}", 0, &session_id, &type, &event, &response));
   ASSERT_EQ(internal::kEventMessageType, type);
   ASSERT_STREQ("method", event.method.c_str());
   ASSERT_TRUE(event.params->is_dict());
 }
 
+TEST(ParseInspectorMessage, EventNoParamsWithSessionId) {
+  internal::InspectorMessageType type;
+  internal::InspectorEvent event;
+  internal::InspectorCommandResponse response;
+  std::string session_id;
+  ASSERT_TRUE(internal::ParseInspectorMessage(
+      "{\"method\":\"method\",\"sessionId\":\"B221AF2\"}", 0, &session_id,
+      &type, &event, &response));
+  ASSERT_EQ(internal::kEventMessageType, type);
+  ASSERT_STREQ("method", event.method.c_str());
+  ASSERT_TRUE(event.params->is_dict());
+  EXPECT_EQ("B221AF2", session_id);
+}
+
 TEST(ParseInspectorMessage, EventWithParams) {
   internal::InspectorMessageType type;
   internal::InspectorEvent event;
   internal::InspectorCommandResponse response;
+  std::string session_id;
   ASSERT_TRUE(internal::ParseInspectorMessage(
-      "{\"method\":\"method\",\"params\":{\"key\":100}}",
-      0, &type, &event, &response));
+      "{\"method\":\"method\",\"params\":{\"key\":100},\"sessionId\":\"AB3A\"}",
+      0, &session_id, &type, &event, &response));
   ASSERT_EQ(internal::kEventMessageType, type);
   ASSERT_STREQ("method", event.method.c_str());
   int key;
   ASSERT_TRUE(event.params->GetInteger("key", &key));
   ASSERT_EQ(100, key);
+  EXPECT_EQ("AB3A", session_id);
 }
 
 TEST(ParseInspectorMessage, CommandNoErrorOrResult) {
   internal::InspectorMessageType type;
   internal::InspectorEvent event;
   internal::InspectorCommandResponse response;
+  std::string session_id;
   // As per Chromium issue 392577, DevTools does not necessarily return a
   // "result" dictionary for every valid response. If neither "error" nor
   // "result" keys are present, a blank result dictionary should be inferred.
-  ASSERT_TRUE(internal::ParseInspectorMessage(
-      "{\"id\":1}", 0, &type, &event, &response));
+  ASSERT_TRUE(
+      internal::ParseInspectorMessage("{\"id\":1,\"sessionId\":\"AB2AF3C\"}", 0,
+                                      &session_id, &type, &event, &response));
   ASSERT_TRUE(response.result->empty());
+  EXPECT_EQ("AB2AF3C", session_id);
 }
 
 TEST(ParseInspectorMessage, CommandError) {
   internal::InspectorMessageType type;
   internal::InspectorEvent event;
   internal::InspectorCommandResponse response;
+  std::string session_id;
   ASSERT_TRUE(internal::ParseInspectorMessage(
-      "{\"id\":1,\"error\":{}}", 0, &type, &event, &response));
+      "{\"id\":1,\"error\":{}}", 0, &session_id, &type, &event, &response));
   ASSERT_EQ(internal::kCommandResponseMessageType, type);
   ASSERT_EQ(1, response.id);
   ASSERT_TRUE(response.error.length());
@@ -564,8 +594,10 @@
   internal::InspectorMessageType type;
   internal::InspectorEvent event;
   internal::InspectorCommandResponse response;
-  ASSERT_TRUE(internal::ParseInspectorMessage(
-      "{\"id\":1,\"result\":{\"key\":1}}", 0, &type, &event, &response));
+  std::string session_id;
+  ASSERT_TRUE(
+      internal::ParseInspectorMessage("{\"id\":1,\"result\":{\"key\":1}}", 0,
+                                      &session_id, &type, &event, &response));
   ASSERT_EQ(internal::kCommandResponseMessageType, type);
   ASSERT_EQ(1, response.id);
   ASSERT_FALSE(response.error.length());
diff --git a/chrome/test/chromedriver/chrome/frame_tracker.cc b/chrome/test/chromedriver/chrome/frame_tracker.cc
index f4e7866..7bfdc48 100644
--- a/chrome/test/chromedriver/chrome/frame_tracker.cc
+++ b/chrome/test/chromedriver/chrome/frame_tracker.cc
@@ -62,6 +62,7 @@
   // Enable target events to allow tracking iframe targets creation.
   base::DictionaryValue params;
   params.SetBoolean("autoAttach", true);
+  params.SetBoolean("flatten", true);
   params.SetBoolean("waitForDebuggerOnStart", false);
   Status status = client->SendCommand("Target.setAutoAttach", params);
   if (status.IsError())
diff --git a/chrome/test/chromedriver/net/sync_websocket_impl.cc b/chrome/test/chromedriver/net/sync_websocket_impl.cc
index b66f091f..8e77e23 100644
--- a/chrome/test/chromedriver/net/sync_websocket_impl.cc
+++ b/chrome/test/chromedriver/net/sync_websocket_impl.cc
@@ -134,37 +134,14 @@
   base::Optional<base::Value> message_value =
       base::JSONReader::Read(message, base::JSON_REPLACE_INVALID_CHARACTERS);
   base::DictionaryValue* message_dict;
-  int id;
-
   if (!message_value || !message_value->GetAsDictionary(&message_dict)) {
     *send_to_chromedriver = true;
     return;
   }
-  if (!message_dict->HasKey("id")) {
-    std::string method;
-    // OOPIFs have responses wrapped in a Target.receivedMessageFromTarget
-    // event. To determine if the response is intended for ChromeDriver, the ID
-    // inside of the wrapped message must be inspected
-    if (!message_dict->GetString("method", &method) ||
-        method != "Target.receivedMessageFromTarget") {
-      *send_to_chromedriver = true;
-      return;
-    }
-    std::string inner_message;
-    base::DictionaryValue* event_params;
-
-    // Send to ChromeDriver so that it properly errors for improper response
-    if (!message_dict->GetDictionary("params", &event_params) ||
-        !event_params->GetString("message", &inner_message)) {
-      *send_to_chromedriver = true;
-      return;
-    }
-    DetermineRecipient(inner_message, send_to_chromedriver);
-  } else {
-    message_dict->GetInteger("id", &id);
-    *send_to_chromedriver = CommandId::IsChromeDriverCommandId(id);
-    return;
-  }
+  int id;
+  *send_to_chromedriver =
+      !message_dict->HasKey("id") || (message_dict->GetInteger("id", &id) &&
+                                      CommandId::IsChromeDriverCommandId(id));
 }
 
 void SyncWebSocketImpl::Core::OnClose() {
diff --git a/chrome/test/chromedriver/net/sync_websocket_impl_unittest.cc b/chrome/test/chromedriver/net/sync_websocket_impl_unittest.cc
index e0b709735..5f82eb0 100644
--- a/chrome/test/chromedriver/net/sync_websocket_impl_unittest.cc
+++ b/chrome/test/chromedriver/net/sync_websocket_impl_unittest.cc
@@ -77,16 +77,12 @@
   SyncWebSocketImpl sock(context_getter_.get());
   ASSERT_TRUE(sock.Connect(server_.web_socket_url()));
   std::string message_for_chromedriver = R"({
-        "method": "Target.receivedMessageFromTarget",
-        "params": {
-           "message": "{\"id\": 1}"
-        }
+        "id": 1,
+        "method": "Page.enable"
       })";
   std::string message_not_for_chromedriver = R"({
-        "method": "Target.receivedMessageFromTarget",
-        "params": {
-           "message": "{\"id\": -1}"
-        }
+        "id": -1,
+        "method": "Page.enable"
       })";
   sock.Send(message_not_for_chromedriver);
   sock.Send(message_for_chromedriver);
@@ -96,21 +92,14 @@
 
   // Getting message id and method
   base::DictionaryValue* message_dict;
-  base::DictionaryValue* inner_dict;
-  std::string method;
-  std::string inner_message;
-  int id;
   base::Optional<base::Value> message_value = base::JSONReader::Read(message);
+  ASSERT_TRUE(message_value.has_value());
   ASSERT_TRUE(message_value->GetAsDictionary(&message_dict));
+  std::string method;
   ASSERT_TRUE(message_dict->GetString("method", &method));
-  ASSERT_TRUE(message_dict->GetDictionary("params", &inner_dict) &&
-              inner_dict->GetString("message", &inner_message));
-  base::Optional<base::Value> inner_message_value =
-      base::JSONReader::Read(inner_message);
-  ASSERT_TRUE(inner_message_value->GetAsDictionary(&message_dict));
+  int id;
   ASSERT_TRUE(message_dict->GetInteger("id", &id));
-
-  ASSERT_STREQ(method.c_str(), "Target.receivedMessageFromTarget");
+  ASSERT_EQ(method, "Page.enable");
   ASSERT_EQ(id, 1);
 }
 
diff --git a/chrome/test/data/extensions/api_test/printing_metrics.crx b/chrome/test/data/extensions/api_test/printing_metrics.crx
new file mode 100644
index 0000000..393361898
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/printing_metrics.crx
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/printing_metrics.pem b/chrome/test/data/extensions/api_test/printing_metrics.pem
new file mode 100644
index 0000000..0abec696
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/printing_metrics.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8PhT/gPpbk9uL
+/xUvylB0QyIlr/zU9+UtYCDsiT0KqouuusP8FtOex4pyt+be6/xljjtcw5j/WRmk
+zK+UOT2HfpKTqiFnPkHAmMycEPzaYeJWskiGSY/8pHiD2x4hQBqgZTg7q+4iUNMo
+psH7uKVrXJqvbzqqYM0aZPL9mj+wTrnR9jcoknaRUil/liFBPm5+lhok9LwRtyzg
+Qd3zUnhegYDJ5tv4P3Wl76uSGDWXXTKjg8BMyoCCFH9/QJmFAXvsrrynwaE/PDAJ
+ieGR1qcMOy3kHBGQjaEebmTNaxjBcKc8iuP/mCYANQ4ktWwdn8ikTJdrogsL2lIv
+v9FUIx/7AgMBAAECggEAF1qxGo/4alMvn7ClLW7cHh3UHI6Mf37v689rgPJ35HJg
+mRbVHi9WJZ1brmblhE5ihoD9GWxaTNfTktAi5RtYPblfhbrLoSD8DGEzWsDQILyY
+gFQRzmNNs1+pD1wojUyENoEdrGg6HP91FdaL5jyJfVvvBF6i4yTWXXTHzDHjvpjs
+rrW143b4LbcpstBatgNHefBPvJL9bBdqvBpceREWE4HIXtj1B3Ac8vhnvNcH3eZF
+909/VO4Xugp+8MHm+m2onFTvRkDkl/JWSPmJHmftuwMxBP2JcarZfcaJ0V568jrZ
+Op1IA/y+PasowN3zHX2lAFnkkpvtza1JGm1qWjpllQKBgQDb1rMFd3Tge4oA+/YL
+UGEZMf2PfgZkSLIt1Q0PxvvRO4ybau/MTeNkEp7wig0mE+WIP7s3ebdhlY+jBoNg
+qDvS3LWzKkL1jNsy1jqGqwYxuEmq1c9QO3GlfBV1+b96s2JWm8fdoiWoMTkBU7i0
+cimuYMMCOQa5aKM7sJHUcSo2/wKBgQDbNOIw0gxWowYSNoRF6Z+R/t8BRT8k7dNN
+cj5SbF8+agnlKC5jvpQ/MdjsXF9werR2V6BBXGOHXcDbTHiHrMYnBic887CXAXIC
+jF8GxnpE0l0d0Kkmd3SedJKILYkBX40QkUas+GynfgjIchcIyFCCeBbhOT6pOcox
+cPqx7+TzBQKBgQC1sFf8qKG79d40ugi7iQ72184MDcgSkdJQ9sf4xifQ4TpwKI1P
+eP/58TnS6wW69q65UJLWYo4g5I712agy9lebCjZRgRgeAPAYr91m92oDJaAcxOC2
+GqrubgL+og6Sxjb8BXvFvQEKZMQMSLlayQ+Rwv7ok2DvU3+1EVU1EuIk2QKBgBQG
+cb9qAOyNOgnB5zWH0ScCNJcmH02dWFdT33OiKNVH1J2VnR9JkrtvL1TwX1ukKgZB
+nQc7jHAaVXrzMnOaigOYoU8FKBWzcRngfvHcgeD1osINhNZZxVKQba55EnIIq5de
+3ikBDNQERIMjQQ1xV0GD/PsYQqws9lar7osA0laNAoGANs4auFpqjoaRNypgO2G2
+GqQV/Y4GAqZRxjsceZNdJ0wa+h5fkYpCEadIDQtL76hpX84opbhTuoBGNpkMeRAH
+y+zRRRUqBB/dG/95C5gboEDfDYbTYvJLHFPIJ/uYyh77QoKIZdHmCKGO2VR/xur/
+psX82yq4i8lQnwaFyiZM0Cw=
+-----END PRIVATE KEY-----
diff --git a/chrome/test/data/extensions/api_test/printing_metrics/api_not_available.html b/chrome/test/data/extensions/api_test/printing_metrics/api_not_available.html
new file mode 100644
index 0000000..d8927531
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/printing_metrics/api_not_available.html
@@ -0,0 +1,6 @@
+<!--
+ * 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.
+-->
+<script src="api_not_available.js"></script>
diff --git a/chrome/test/data/extensions/api_test/printing_metrics/api_not_available.js b/chrome/test/data/extensions/api_test/printing_metrics/api_not_available.js
new file mode 100644
index 0000000..02be1fd
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/printing_metrics/api_not_available.js
@@ -0,0 +1,6 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.test.assertTrue(!chrome.printingMetrics);
+chrome.test.succeed();
diff --git a/chrome/test/data/extensions/api_test/printing_metrics/get_print_jobs.html b/chrome/test/data/extensions/api_test/printing_metrics/get_print_jobs.html
new file mode 100644
index 0000000..61dc2db
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/printing_metrics/get_print_jobs.html
@@ -0,0 +1,6 @@
+<!--
+ * 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.
+-->
+<script src="get_print_jobs.js"></script>
diff --git a/chrome/test/data/extensions/api_test/printing_metrics/get_print_jobs.js b/chrome/test/data/extensions/api_test/printing_metrics/get_print_jobs.js
new file mode 100644
index 0000000..ed9203df
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/printing_metrics/get_print_jobs.js
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function testGetPrintJobs(expectedTitle) {
+  chrome.test.runTests([() => {
+    chrome.printingMetrics.getPrintJobs(printJobs => {
+      chrome.test.assertEq(1, printJobs.length);
+      chrome.test.assertEq(expectedTitle, printJobs[0].title);
+      chrome.test.succeed();
+    });
+  }]);
+}
+
+chrome.test.getConfig(config => {
+  testGetPrintJobs(config.customArg);
+});
diff --git a/chrome/test/data/extensions/api_test/printing_metrics/manifest.json b/chrome/test/data/extensions/api_test/printing_metrics/manifest.json
new file mode 100644
index 0000000..a6e87270
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/printing_metrics/manifest.json
@@ -0,0 +1,9 @@
+{
+  "name": "chrome.printingMetrics",
+  "version": "0.1",
+  "manifest_version": 2,
+  "description": "Browser tests for chrome.printingMetrics API",
+  "permissions": [
+    "printingMetrics"
+  ]
+}
diff --git a/chrome/test/data/extensions/api_test/printing_metrics/on_print_job_finished.html b/chrome/test/data/extensions/api_test/printing_metrics/on_print_job_finished.html
new file mode 100644
index 0000000..5bacb5f
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/printing_metrics/on_print_job_finished.html
@@ -0,0 +1,6 @@
+<!--
+ * 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.
+-->
+<script src="on_print_job_finished.js"></script>
diff --git a/chrome/test/data/extensions/api_test/printing_metrics/on_print_job_finished.js b/chrome/test/data/extensions/api_test/printing_metrics/on_print_job_finished.js
new file mode 100644
index 0000000..ab0d993
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/printing_metrics/on_print_job_finished.js
@@ -0,0 +1,8 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.printingMetrics.onPrintJobFinished.addListener(printJobInfo => {
+  chrome.test.assertEqual('title', printJobInfo.title);
+  chrome.test.notifyPass();
+});
diff --git a/chrome/test/data/extensions/api_test/printing_metrics/update_manifest.xml b/chrome/test/data/extensions/api_test/printing_metrics/update_manifest.xml
new file mode 100644
index 0000000..f06a638
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/printing_metrics/update_manifest.xml
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  This update manifest points to the enterprise_device_attributes.crx file.
+  "mock.http" is a placeholder that gets substituted with the test server
+  address in runtime.
+-->
+<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
+  <app appid='cmgkkmeeoiceijkpmaabbmpgnkpaaela'>
+    <updatecheck
+        codebase='http://mock.http/extensions/api_test/printing_metrics.crx'
+        version='1.0.0.0' />
+   </app>
+</gupdate>
diff --git a/chrome/test/data/local_ntp/realbox_browsertest.js b/chrome/test/data/local_ntp/realbox_browsertest.js
index 9ae8241f..01dc9d2 100644
--- a/chrome/test/data/local_ntp/realbox_browsertest.js
+++ b/chrome/test/data/local_ntp/realbox_browsertest.js
@@ -19,6 +19,7 @@
  * @const
  */
 test.realbox.CLASSES = {
+  SELECTED: 'selected',
   SHOW_MATCHES: 'show-matches',
 };
 
@@ -84,6 +85,9 @@
 /** @type {!Array<string>} */
 test.realbox.queries;
 
+/** @type {!Array<number>} */
+test.realbox.deletedLines;
+
 /** @type {!Element} */
 test.realbox.realboxEl;
 
@@ -101,10 +105,14 @@
       queryAutocomplete(query) {
         test.realbox.queries.push(query);
       },
+      deleteAutocompleteMatch(line) {
+        test.realbox.deletedLines.push(line);
+      }
     },
   };
 
   test.realbox.queries = [];
+  test.realbox.deletedLines = [];
 
   initLocalNTP(/*isGooglePage=*/ true);
 
@@ -465,3 +473,90 @@
   assertEquals(RESULTS.length, newMatchesEl.children.length);
   assertFalse(matchesEl === newMatchesEl);
 };
+
+test.realbox.testDeleteAutocompleteResultUnmodifiedDelete = function() {
+  const keyEvent = new KeyboardEvent('keydown', {
+    bubbles: true,
+    cancelable: true,
+    key: 'Delete',
+  });
+  test.realbox.realboxEl.dispatchEvent(keyEvent);
+  assertFalse(keyEvent.defaultPrevented);
+};
+
+test.realbox.testDeleteAutocompleteResultShiftDeleteWithNoMatches = function() {
+  const keyEvent = new KeyboardEvent('keydown', {
+    bubbles: true,
+    cancelable: true,
+    key: 'Delete',
+    shiftKey: true,
+  });
+  test.realbox.realboxEl.dispatchEvent(keyEvent);
+  assertFalse(keyEvent.defaultPrevented);
+};
+
+test.realbox.testDeleteAutocompleteResultShiftDeleteCantRemove = function() {
+  test.realbox.realboxEl.value = 'hello world';
+  test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
+
+  const matches = [test.realbox.getSearchMatch(), test.realbox.getUrlMatch()];
+  chrome.embeddedSearch.searchBox.onqueryautocompletedone(
+      {input: test.realbox.realboxEl.value, matches});
+
+  const keyEvent = new KeyboardEvent('keydown', {
+    bubbles: true,
+    cancelable: true,
+    key: 'Delete',
+    shiftKey: true,
+  });
+  test.realbox.realboxEl.dispatchEvent(keyEvent);
+  assertTrue(keyEvent.defaultPrevented);
+
+  assertEquals(1, test.realbox.deletedLines.length);
+  assertEquals(0, test.realbox.deletedLines[0]);
+
+  assertEquals(2, $(test.realbox.IDS.REALBOX_MATCHES).children.length);
+
+  chrome.embeddedSearch.searchBox.ondeleteautocompletematch(
+      {success: false, matches: []});
+
+  assertEquals(2, $(test.realbox.IDS.REALBOX_MATCHES).children.length);
+};
+
+test.realbox.testDeleteAutocompleteResultShiftDeleteCanRemove = function() {
+  test.realbox.realboxEl.value = 'hello world';
+  test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
+
+  const matches = [test.realbox.getSearchMatch(), test.realbox.getUrlMatch()];
+  chrome.embeddedSearch.searchBox.onqueryautocompletedone(
+      {input: test.realbox.realboxEl.value, matches});
+
+  const downArrow = new KeyboardEvent('keydown', {
+    bubbles: true,
+    cancelable: true,
+    key: 'ArrowDown',
+  });
+  test.realbox.realboxEl.dispatchEvent(downArrow);
+  assertTrue(downArrow.defaultPrevented);
+
+  const matchEls = $(test.realbox.IDS.REALBOX_MATCHES).children;
+  assertEquals(2, matchEls.length);
+  assertTrue(matchEls[1].classList.contains(test.realbox.CLASSES.SELECTED));
+
+  const shiftDelete = new KeyboardEvent('keydown', {
+    bubbles: true,
+    cancelable: true,
+    key: 'Delete',
+    shiftKey: true,
+  });
+  test.realbox.realboxEl.dispatchEvent(shiftDelete);
+  assertTrue(shiftDelete.defaultPrevented);
+
+  assertEquals(1, test.realbox.deletedLines.length);
+  assertEquals(1, test.realbox.deletedLines[0]);
+
+  chrome.embeddedSearch.searchBox.ondeleteautocompletematch(
+      {success: true, matches: [test.realbox.getSearchMatch()]});
+
+  assertEquals(1, $(test.realbox.IDS.REALBOX_MATCHES).children.length);
+};
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_v3_browsertest.js b/chrome/test/data/webui/cr_elements/cr_elements_v3_browsertest.js
index 0e49ad8..a127b70e 100644
--- a/chrome/test/data/webui/cr_elements/cr_elements_v3_browsertest.js
+++ b/chrome/test/data/webui/cr_elements/cr_elements_v3_browsertest.js
@@ -87,9 +87,18 @@
   }
 };
 
-TEST_F('CrElementsDrawerV3Test', 'All', function() {
-  mocha.run();
-});
+// https://crbug.com/1008122
+GEN('#if defined(OS_MACOSX) && defined(NDEBUG)');
+GEN('# define MAYBE_CrElementsDrawerV3Test_All \\');
+GEN('     DISABLED_All');
+GEN('#else');
+GEN('# define MAYBE_CrElementsDrawerV3Test_All  \\');
+GEN('     All');
+GEN('#endif');
+TEST_F(
+    'CrElementsDrawerV3Test', 'MAYBE_CrElementsDrawerV3Test_All', function() {
+      mocha.run();
+    });
 
 // eslint-disable-next-line no-var
 var CrElementsExpandButtonV3Test = class extends CrElementsV3BrowserTest {
diff --git a/chromecast/net/BUILD.gn b/chromecast/net/BUILD.gn
index 235aeea..926bf45 100644
--- a/chromecast/net/BUILD.gn
+++ b/chromecast/net/BUILD.gn
@@ -71,6 +71,7 @@
   ]
 
   deps = [
+    ":io_buffer_pool",
     "//base",
   ]
 }
@@ -99,10 +100,12 @@
   sources = [
     "fake_stream_socket_unittest.cc",
     "io_buffer_pool_unittest.cc",
+    "small_message_socket_unittest.cc",
   ]
 
   deps = [
     ":io_buffer_pool",
+    ":small_message_socket",
     ":test_support",
     "//base",
     "//base/test:run_all_unittests",
diff --git a/chromecast/net/fake_stream_socket.cc b/chromecast/net/fake_stream_socket.cc
index 13d79117..42bfecee 100644
--- a/chromecast/net/fake_stream_socket.cc
+++ b/chromecast/net/fake_stream_socket.cc
@@ -89,6 +89,10 @@
   peer_ = peer;
 }
 
+void FakeStreamSocket::SetBadSenderMode(bool bad_sender) {
+  bad_sender_mode_ = bad_sender;
+}
+
 int FakeStreamSocket::Read(net::IOBuffer* buf,
                            int buf_len,
                            net::CompletionOnceCallback callback) {
@@ -105,8 +109,12 @@
   if (!peer_) {
     return net::ERR_SOCKET_NOT_CONNECTED;
   }
-  peer_->buffer_->Write(buf->data(), buf_len);
-  return buf_len;
+  int amount_to_send = buf_len;
+  if (bad_sender_mode_) {
+    amount_to_send = std::min(buf_len, buf_len / 2 + 1);
+  }
+  peer_->buffer_->Write(buf->data(), amount_to_send);
+  return amount_to_send;
 }
 
 int FakeStreamSocket::SetReceiveBufferSize(int32_t /* size */) {
diff --git a/chromecast/net/fake_stream_socket.h b/chromecast/net/fake_stream_socket.h
index 9ca0c649..d5061b38 100644
--- a/chromecast/net/fake_stream_socket.h
+++ b/chromecast/net/fake_stream_socket.h
@@ -27,6 +27,10 @@
   // Sets the peer for this socket.
   void SetPeer(FakeStreamSocket* peer);
 
+  // Enables/disables "bad sender mode", where Write() will always try to send
+  // less than the full buffer. Disabled by default.
+  void SetBadSenderMode(bool bad_sender);
+
   // net::StreamSocket implementation:
   int Read(net::IOBuffer* buf,
            int buf_len,
@@ -60,6 +64,7 @@
   const std::unique_ptr<SocketBuffer> buffer_;
   FakeStreamSocket* peer_;
   net::NetLogWithSource net_log_;
+  bool bad_sender_mode_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(FakeStreamSocket);
 };
diff --git a/chromecast/net/small_message_socket.cc b/chromecast/net/small_message_socket.cc
index 864e5c9..c793f5a 100644
--- a/chromecast/net/small_message_socket.cc
+++ b/chromecast/net/small_message_socket.cc
@@ -17,6 +17,7 @@
 #include "base/logging.h"
 #include "base/sequenced_task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "chromecast/net/io_buffer_pool.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "net/socket/socket.h"
@@ -33,7 +34,7 @@
 
 }  // namespace
 
-class SmallMessageSocket::WriteBuffer : public ::net::IOBuffer {
+class SmallMessageSocket::BufferWrapper : public ::net::IOBuffer {
  public:
   void SetUnderlyingBuffer(scoped_refptr<IOBuffer> base, size_t size) {
     base_ = std::move(base);
@@ -42,38 +43,109 @@
     data_ = base_->data();
   }
 
+  scoped_refptr<IOBuffer> TakeUnderlyingBuffer() { return std::move(base_); }
+
+  void ClearUnderlyingBuffer() {
+    data_ = nullptr;
+    base_.reset();
+  }
+
   void DidConsume(size_t bytes) {
     used_ += bytes;
     data_ = base_->data() + used_;
   }
 
-  size_t BytesRemaining() { return size_ - used_; }
+  char* StartOfBuffer() const {
+    DCHECK(base_);
+    return base_->data();
+  }
+
+  size_t size() const { return size_; }
+  size_t used() const { return used_; }
+  size_t remaining() const {
+    DCHECK_GE(size_, used_);
+    return size_ - used_;
+  }
 
  private:
-  ~WriteBuffer() override { data_ = nullptr; }
+  ~BufferWrapper() override { data_ = nullptr; }
 
   scoped_refptr<IOBuffer> base_;
-  size_t size_;
-  size_t used_;
+  size_t size_ = 0;
+  size_t used_ = 0;
 };
 
 SmallMessageSocket::SmallMessageSocket(std::unique_ptr<net::Socket> socket)
     : socket_(std::move(socket)),
       task_runner_(base::SequencedTaskRunnerHandle::Get()),
-      write_buffer_(base::MakeRefCounted<WriteBuffer>()),
-      weak_factory_(this) {}
+      write_storage_(base::MakeRefCounted<net::GrowableIOBuffer>()),
+      write_buffer_(base::MakeRefCounted<BufferWrapper>()),
+      read_storage_(base::MakeRefCounted<net::GrowableIOBuffer>()),
+      read_buffer_(base::MakeRefCounted<BufferWrapper>()),
+      weak_factory_(this) {
+  write_storage_->SetCapacity(kDefaultBufferSize);
+  read_storage_->SetCapacity(kDefaultBufferSize);
+}
 
 SmallMessageSocket::~SmallMessageSocket() = default;
 
-void* SmallMessageSocket::PrepareSend(int message_size) {
-  DCHECK_LE(message_size, std::numeric_limits<uint16_t>::max());
-  if (write_buffer_->BytesRemaining()) {
-    send_blocked_ = true;
-    return nullptr;
+void SmallMessageSocket::UseBufferPool(
+    scoped_refptr<IOBufferPool> buffer_pool) {
+  DCHECK(buffer_pool);
+  if (buffer_pool_) {
+    // Replace existing buffer pool. No need to copy data out of existing buffer
+    // since it will remain valid until we are done using it.
+    buffer_pool_ = std::move(buffer_pool);
+    return;
   }
 
-  if (!write_storage_) {
-    write_storage_ = base::MakeRefCounted<net::GrowableIOBuffer>();
+  buffer_pool_ = std::move(buffer_pool);
+  if (!in_message_) {
+    ActivateBufferPool(read_storage_->StartOfBuffer(), read_storage_->offset());
+  }
+}
+
+void SmallMessageSocket::ActivateBufferPool(char* current_data,
+                                            size_t current_size) {
+  // Copy any already-read data into a new buffer for pool-based operation.
+  DCHECK(buffer_pool_);
+  DCHECK(!in_message_);
+
+  scoped_refptr<::net::IOBuffer> new_buffer;
+  size_t new_buffer_size;
+  if (current_size <= buffer_pool_->buffer_size()) {
+    new_buffer = buffer_pool_->GetBuffer();
+    new_buffer_size = buffer_pool_->buffer_size();
+  } else {
+    new_buffer = base::MakeRefCounted<::net::IOBuffer>(current_size * 2);
+    new_buffer_size = current_size * 2;
+  }
+  memcpy(new_buffer->data(), current_data, current_size);
+
+  read_buffer_->SetUnderlyingBuffer(std::move(new_buffer), new_buffer_size);
+  read_buffer_->DidConsume(current_size);
+}
+
+void SmallMessageSocket::RemoveBufferPool() {
+  if (!buffer_pool_) {
+    return;
+  }
+
+  if (static_cast<size_t>(read_storage_->capacity()) < read_buffer_->used()) {
+    read_storage_->SetCapacity(read_buffer_->used());
+  }
+  memcpy(read_storage_->StartOfBuffer(), read_buffer_->StartOfBuffer(),
+         read_buffer_->used());
+  read_storage_->set_offset(read_buffer_->used());
+
+  buffer_pool_.reset();
+}
+
+void* SmallMessageSocket::PrepareSend(int message_size) {
+  DCHECK_LE(message_size, std::numeric_limits<uint16_t>::max());
+  if (write_buffer_->remaining()) {
+    send_blocked_ = true;
+    return nullptr;
   }
 
   write_storage_->set_offset(0);
@@ -90,7 +162,7 @@
 
 bool SmallMessageSocket::SendBuffer(scoped_refptr<net::IOBuffer> data,
                                     int size) {
-  if (write_buffer_->BytesRemaining()) {
+  if (write_buffer_->remaining()) {
     send_blocked_ = true;
     return false;
   }
@@ -103,7 +175,7 @@
 void SmallMessageSocket::Send() {
   for (int i = 0; i < kMaxIOLoop; ++i) {
     int result =
-        socket_->Write(write_buffer_.get(), write_buffer_->BytesRemaining(),
+        socket_->Write(write_buffer_.get(), write_buffer_->remaining(),
                        base::BindOnce(&SmallMessageSocket::OnWriteComplete,
                                       base::Unretained(this)),
                        MISSING_TRAFFIC_ANNOTATION);
@@ -132,10 +204,11 @@
   }
 
   write_buffer_->DidConsume(result);
-  if (write_buffer_->BytesRemaining()) {
+  if (write_buffer_->remaining()) {
     return true;
   }
 
+  write_buffer_->ClearUnderlyingBuffer();
   if (send_blocked_) {
     send_blocked_ = false;
     OnSendUnblocked();
@@ -152,19 +225,17 @@
 }
 
 void SmallMessageSocket::ReceiveMessages() {
-  if (!read_buffer_) {
-    read_buffer_ = base::MakeRefCounted<net::GrowableIOBuffer>();
-    read_buffer_->SetCapacity(kDefaultBufferSize);
-  }
   // Post a task rather than just calling Read(), to avoid calling delegate
   // methods from within this method.
-  task_runner_->PostTask(FROM_HERE,
-                         base::BindOnce(&SmallMessageSocket::StartReading,
-                                        weak_factory_.GetWeakPtr()));
+  task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&SmallMessageSocket::ReceiveMessagesSynchronously,
+                     weak_factory_.GetWeakPtr()));
 }
 
-void SmallMessageSocket::StartReading() {
-  if (HandleCompletedMessages()) {
+void SmallMessageSocket::ReceiveMessagesSynchronously() {
+  if ((buffer_pool_ && HandleCompletedMessageBuffers()) ||
+      (!buffer_pool_ && HandleCompletedMessages())) {
     Read();
   }
 }
@@ -174,18 +245,28 @@
   // This improves average packet receive delay as compared to always posting a
   // new task for each call to Read().
   for (int i = 0; i < kMaxIOLoop; ++i) {
+    net::IOBuffer* buffer;
+    int size;
+    if (buffer_pool_) {
+      buffer = read_buffer_.get();
+      size = read_buffer_->remaining();
+    } else {
+      buffer = read_storage_.get();
+      size = read_storage_->RemainingCapacity();
+    }
     int read_result =
-        socket_->Read(read_buffer_.get(), read_buffer_->RemainingCapacity(),
-                      base::BindOnce(&SmallMessageSocket::OnReadComplete,
-                                     base::Unretained(this)));
+        socket()->Read(buffer, size,
+                       base::BindOnce(&SmallMessageSocket::OnReadComplete,
+                                      base::Unretained(this)));
 
     if (!HandleReadResult(read_result)) {
       return;
     }
   }
 
-  task_runner_->PostTask(FROM_HERE, base::BindOnce(&SmallMessageSocket::Read,
-                                                   weak_factory_.GetWeakPtr()));
+  task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&SmallMessageSocket::Read, weak_factory_.GetWeakPtr()));
 }
 
 void SmallMessageSocket::OnReadComplete(int result) {
@@ -209,51 +290,120 @@
     return false;
   }
 
-  read_buffer_->set_offset(read_buffer_->offset() + result);
-  return HandleCompletedMessages();
+  if (buffer_pool_) {
+    read_buffer_->DidConsume(result);
+    return HandleCompletedMessageBuffers();
+  } else {
+    read_storage_->set_offset(read_storage_->offset() + result);
+    return HandleCompletedMessages();
+  }
 }
 
 bool SmallMessageSocket::HandleCompletedMessages() {
-  size_t total_size = read_buffer_->offset();
-  char* start_ptr = read_buffer_->StartOfBuffer();
+  DCHECK(!buffer_pool_);
   bool keep_reading = true;
-
-  while (total_size >= sizeof(uint16_t)) {
+  size_t bytes_read = read_storage_->offset();
+  char* start_ptr = read_storage_->StartOfBuffer();
+  while (bytes_read >= sizeof(uint16_t) && keep_reading) {
     uint16_t message_size;
     base::ReadBigEndian(start_ptr, &message_size);
 
-    if (static_cast<size_t>(read_buffer_->capacity()) <
-        sizeof(uint16_t) + message_size) {
-      int position = start_ptr - read_buffer_->StartOfBuffer();
-      read_buffer_->SetCapacity(sizeof(uint16_t) + message_size);
-      start_ptr = read_buffer_->StartOfBuffer() + position;
+    size_t required_size = sizeof(uint16_t) + message_size;
+    if (static_cast<size_t>(read_storage_->capacity()) < required_size) {
+      if (start_ptr != read_storage_->StartOfBuffer()) {
+        memmove(read_storage_->StartOfBuffer(), start_ptr, bytes_read);
+        read_storage_->set_offset(bytes_read);
+      }
+      read_storage_->SetCapacity(required_size);
+      return true;
     }
 
-    if (total_size < sizeof(uint16_t) + message_size) {
+    if (bytes_read < required_size) {
       break;  // Haven't received the full message yet.
     }
 
     // Take a weak pointer in case OnMessage() causes this to be deleted.
     auto self = weak_factory_.GetWeakPtr();
+    in_message_ = true;
     keep_reading = OnMessage(start_ptr + sizeof(uint16_t), message_size);
     if (!self) {
       return false;
     }
+    in_message_ = false;
 
-    total_size -= sizeof(uint16_t) + message_size;
-    start_ptr += sizeof(uint16_t) + message_size;
+    start_ptr += required_size;
+    bytes_read -= required_size;
 
-    if (!keep_reading) {
-      break;
+    if (buffer_pool_) {
+      // A buffer pool was added within OnMessage().
+      ActivateBufferPool(start_ptr, bytes_read);
+      return (keep_reading ? HandleCompletedMessageBuffers() : false);
     }
   }
 
-  if (start_ptr != read_buffer_->StartOfBuffer()) {
-    memmove(read_buffer_->StartOfBuffer(), start_ptr, total_size);
-    read_buffer_->set_offset(total_size);
+  if (start_ptr != read_storage_->StartOfBuffer()) {
+    memmove(read_storage_->StartOfBuffer(), start_ptr, bytes_read);
+    read_storage_->set_offset(bytes_read);
   }
 
   return keep_reading;
 }
 
+bool SmallMessageSocket::HandleCompletedMessageBuffers() {
+  DCHECK(buffer_pool_);
+  size_t bytes_read;
+  while ((bytes_read = read_buffer_->used()) >= sizeof(uint16_t)) {
+    uint16_t message_size;
+    base::ReadBigEndian(read_buffer_->StartOfBuffer(), &message_size);
+
+    size_t required_size = sizeof(uint16_t) + message_size;
+    if (read_buffer_->size() < required_size) {
+      // Current buffer is not big enough.
+      auto new_buffer = base::MakeRefCounted<::net::IOBuffer>(required_size);
+      memcpy(new_buffer->data(), read_buffer_->StartOfBuffer(), bytes_read);
+      read_buffer_->SetUnderlyingBuffer(std::move(new_buffer), required_size);
+      read_buffer_->DidConsume(bytes_read);
+      return true;
+    }
+
+    if (bytes_read < required_size) {
+      break;  // Haven't received the full message yet.
+    }
+
+    auto old_buffer = read_buffer_->TakeUnderlyingBuffer();
+    auto new_buffer = buffer_pool_->GetBuffer();
+    size_t new_buffer_size = buffer_pool_->buffer_size();
+    size_t extra_size = bytes_read - required_size;
+    if (extra_size > 0) {
+      // Copy extra data to new buffer.
+      if (extra_size > buffer_pool_->buffer_size()) {
+        new_buffer = base::MakeRefCounted<::net::IOBuffer>(extra_size);
+        new_buffer_size = extra_size;
+      }
+      memcpy(new_buffer->data(), old_buffer->data() + required_size,
+             extra_size);
+    }
+    read_buffer_->SetUnderlyingBuffer(std::move(new_buffer), new_buffer_size);
+    read_buffer_->DidConsume(extra_size);
+
+    // Take a weak pointer in case OnMessageBuffer() causes this to be deleted.
+    auto self = weak_factory_.GetWeakPtr();
+    bool keep_reading = OnMessageBuffer(std::move(old_buffer), required_size);
+    if (!self || !keep_reading) {
+      return false;
+    }
+    if (!buffer_pool_) {
+      // The buffer pool was removed within OnMessageBuffer().
+      return HandleCompletedMessages();
+    }
+  }
+
+  return true;
+}
+
+bool SmallMessageSocket::OnMessageBuffer(scoped_refptr<net::IOBuffer> buffer,
+                                         int size) {
+  return OnMessage(buffer->data() + sizeof(uint16_t), size - sizeof(uint16_t));
+}
+
 }  // namespace chromecast
diff --git a/chromecast/net/small_message_socket.h b/chromecast/net/small_message_socket.h
index b765861..c51df5f 100644
--- a/chromecast/net/small_message_socket.h
+++ b/chromecast/net/small_message_socket.h
@@ -22,16 +22,29 @@
 }  // namespace net
 
 namespace chromecast {
+class IOBufferPool;
 
-// Sends and receives small messages (< 64 KB) over a Socket. All methods must
-// be called on the same sequence. Any of the virtual methods can destroy this
-// object if desired.
+// Sends small messages (< 64 KB) over a Socket. All methods must be called on
+// the same sequence. Any of the virtual methods can destroy this object if
+// desired.
 class SmallMessageSocket {
  public:
   explicit SmallMessageSocket(std::unique_ptr<net::Socket> socket);
   virtual ~SmallMessageSocket();
 
-  const net::Socket* socket() const { return socket_.get(); }
+  net::Socket* socket() const { return socket_.get(); }
+  base::SequencedTaskRunner* task_runner() const { return task_runner_.get(); }
+  IOBufferPool* buffer_pool() const { return buffer_pool_.get(); }
+
+  // Adds a |buffer_pool| used to allocate buffers to receive messages into;
+  // received messages are passed to OnMessageBuffer(). If a message would be
+  // too big to fit in a pool-provided buffer, a dynamically allocated IOBuffer
+  // will be used instead for that message.
+  void UseBufferPool(scoped_refptr<IOBufferPool> buffer_pool);
+
+  // Removes the buffer pool; subsequent received messages will be passed to
+  // OnMessage().
+  void RemoveBufferPool();
 
   // Prepares a buffer to send a message of the given |message_size|. Returns
   // nullptr if sending is not allowed right now (ie, another send is currently
@@ -54,10 +67,18 @@
   // Enables receiving messages from the stream. Messages will be received and
   // passed to OnMessage() until either an error occurs, the end of stream is
   // reached, or OnMessage() returns false. If OnMessage() returns false, you
-  // may call ReceiveMessages() to start receiving again.
+  // may call ReceiveMessages() to start receiving again. OnMessage() will not
+  // be called synchronously from within this method (it always posts a task).
   void ReceiveMessages();
 
+  // Same as ReceiveMessages(), but OnMessage() may be called synchronously.
+  // This is more efficient because it doesn't post a task to ensure
+  // asynchronous reads.
+  void ReceiveMessagesSynchronously();
+
  protected:
+  class BufferWrapper;
+
   // Called when sending becomes possible again, if a previous attempt to send
   // was rejected.
   virtual void OnSendUnblocked() {}
@@ -69,31 +90,42 @@
   // Called when the end of stream has been read. No more data will be received.
   virtual void OnEndOfStream() {}
 
-  // Called when a message has been received. The |data| buffer contains |size|
-  // bytes of data.
+  // Called when a message has been received and there is no buffer pool. The
+  // |data| buffer contains |size| bytes of data. Return |true| to continue
+  // reading messages after OnMessage() returns.
   virtual bool OnMessage(char* data, int size) = 0;
 
- private:
-  class WriteBuffer;
+  // Called when a message has been received. The |buffer| contains |size| bytes
+  // of data, which includes the first 2 bytes which are the size in network
+  // byte order. Note that these 2 bytes are not included in OnMessage()!
+  // Return |true| to continue receiving messages.
+  virtual bool OnMessageBuffer(scoped_refptr<net::IOBuffer> buffer, int size);
 
+ private:
   void OnWriteComplete(int result);
   bool HandleWriteResult(int result);
   void PostError(int error);
 
-  void StartReading();
   void Read();
   void OnReadComplete(int result);
   bool HandleReadResult(int result);
   bool HandleCompletedMessages();
+  bool HandleCompletedMessageBuffers();
+  void ActivateBufferPool(char* current_data, size_t current_size);
 
-  std::unique_ptr<net::Socket> socket_;
+  const std::unique_ptr<net::Socket> socket_;
   const scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
-  scoped_refptr<net::GrowableIOBuffer> write_storage_;
-  scoped_refptr<WriteBuffer> write_buffer_;
+  const scoped_refptr<net::GrowableIOBuffer> write_storage_;
+  const scoped_refptr<BufferWrapper> write_buffer_;
   bool send_blocked_ = false;
 
-  scoped_refptr<net::GrowableIOBuffer> read_buffer_;
+  const scoped_refptr<net::GrowableIOBuffer> read_storage_;
+
+  scoped_refptr<IOBufferPool> buffer_pool_;
+  const scoped_refptr<BufferWrapper> read_buffer_;
+
+  bool in_message_ = false;
 
   base::WeakPtrFactory<SmallMessageSocket> weak_factory_;
 
diff --git a/chromecast/net/small_message_socket_unittest.cc b/chromecast/net/small_message_socket_unittest.cc
new file mode 100644
index 0000000..b4a7c60
--- /dev/null
+++ b/chromecast/net/small_message_socket_unittest.cc
@@ -0,0 +1,264 @@
+// 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 "chromecast/net/small_message_socket.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/big_endian.h"
+#include "base/test/task_environment.h"
+#include "chromecast/net/fake_stream_socket.h"
+#include "chromecast/net/io_buffer_pool.h"
+#include "net/base/io_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+
+namespace {
+
+const size_t kDefaultMessageSize = 256;
+
+const char kIpAddress1[] = "192.168.0.1";
+const uint16_t kPort1 = 10001;
+const char kIpAddress2[] = "192.168.0.2";
+const uint16_t kPort2 = 10002;
+
+net::IPAddress IpLiteralToIpAddress(const std::string& ip_literal) {
+  net::IPAddress ip_address;
+  CHECK(ip_address.AssignFromIPLiteral(ip_literal));
+  return ip_address;
+}
+
+void SetData(char* buffer, int size) {
+  for (int i = 0; i < size; ++i) {
+    buffer[i] = static_cast<char>(i);
+  }
+}
+
+void CheckData(char* buffer, int size) {
+  for (int i = 0; i < size; ++i) {
+    EXPECT_EQ(buffer[i], static_cast<char>(i));
+  }
+}
+
+class TestSocket : public SmallMessageSocket {
+ public:
+  explicit TestSocket(std::unique_ptr<net::Socket> socket)
+      : SmallMessageSocket(std::move(socket)) {}
+
+  ~TestSocket() override = default;
+
+  void UseBufferPool() {
+    SmallMessageSocket::UseBufferPool(base::MakeRefCounted<IOBufferPool>(
+        kDefaultMessageSize + sizeof(uint16_t)));
+  }
+  void SwapPoolUse(bool swap) { swap_pool_use_ = swap; }
+
+  size_t last_message_size() const {
+    DCHECK(!message_history_.empty());
+    return message_history_[message_history_.size() - 1];
+  }
+
+  const std::vector<size_t>& message_history() const {
+    return message_history_;
+  }
+
+ private:
+  void OnError(int error) override { NOTREACHED(); }
+
+  bool OnMessage(char* data, int size) override {
+    message_history_.push_back(size);
+    CheckData(data, size);
+    if (swap_pool_use_) {
+      UseBufferPool();
+    }
+    return true;
+  }
+
+  bool OnMessageBuffer(scoped_refptr<net::IOBuffer> buffer, int size) override {
+    uint16_t message_size;
+    base::ReadBigEndian(buffer->data(), &message_size);
+    DCHECK_EQ(message_size, size - sizeof(uint16_t));
+    message_history_.push_back(message_size);
+    CheckData(buffer->data() + sizeof(uint16_t), message_size);
+    if (swap_pool_use_) {
+      RemoveBufferPool();
+    }
+    return true;
+  }
+
+  std::vector<size_t> message_history_;
+  bool swap_pool_use_ = false;
+};
+
+}  // namespace
+
+class SmallMessageSocketTest : public ::testing::Test {
+ public:
+  SmallMessageSocketTest() {
+    auto fake1 = std::make_unique<FakeStreamSocket>(
+        net::IPEndPoint(IpLiteralToIpAddress(kIpAddress1), kPort1));
+    auto fake2 = std::make_unique<FakeStreamSocket>(
+        net::IPEndPoint(IpLiteralToIpAddress(kIpAddress2), kPort2));
+    fake1->SetPeer(fake2.get());
+    fake2->SetPeer(fake1.get());
+    fake1->SetBadSenderMode(true);
+    fake2->SetBadSenderMode(true);
+
+    socket_1_ = std::make_unique<TestSocket>(std::move(fake1));
+    socket_2_ = std::make_unique<TestSocket>(std::move(fake2));
+  }
+
+  ~SmallMessageSocketTest() override = default;
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+
+  std::unique_ptr<TestSocket> socket_1_;
+  std::unique_ptr<TestSocket> socket_2_;
+};
+
+TEST_F(SmallMessageSocketTest, SendAndReceive) {
+  auto buffer = base::MakeRefCounted<net::IOBuffer>(kDefaultMessageSize +
+                                                    sizeof(uint16_t));
+  base::WriteBigEndian(buffer->data(),
+                       static_cast<uint16_t>(kDefaultMessageSize));
+  SetData(buffer->data() + sizeof(uint16_t), kDefaultMessageSize);
+  socket_2_->ReceiveMessages();
+  socket_1_->SendBuffer(std::move(buffer),
+                        kDefaultMessageSize + sizeof(uint16_t));
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(socket_2_->last_message_size(), kDefaultMessageSize);
+}
+
+TEST_F(SmallMessageSocketTest, PrepareSendAndReceive) {
+  char* buffer =
+      static_cast<char*>(socket_1_->PrepareSend(kDefaultMessageSize));
+  SetData(buffer, kDefaultMessageSize);
+  socket_2_->ReceiveMessages();
+  socket_1_->Send();
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(socket_2_->last_message_size(), kDefaultMessageSize);
+}
+
+TEST_F(SmallMessageSocketTest, MultipleMessages) {
+  char* buffer =
+      static_cast<char*>(socket_1_->PrepareSend(kDefaultMessageSize));
+  SetData(buffer, kDefaultMessageSize);
+  socket_1_->Send();
+  task_environment_.RunUntilIdle();
+
+  buffer =
+      static_cast<char*>(socket_1_->PrepareSend(kDefaultMessageSize * 2 + 1));
+  SetData(buffer, kDefaultMessageSize * 2 + 1);
+  socket_1_->Send();
+  task_environment_.RunUntilIdle();
+
+  buffer = static_cast<char*>(socket_1_->PrepareSend(kDefaultMessageSize - 5));
+  SetData(buffer, kDefaultMessageSize - 5);
+  socket_1_->Send();
+  task_environment_.RunUntilIdle();
+
+  socket_2_->ReceiveMessages();
+  task_environment_.RunUntilIdle();
+
+  ASSERT_EQ(socket_2_->message_history().size(), 3u);
+  EXPECT_EQ(socket_2_->message_history()[0], kDefaultMessageSize);
+  EXPECT_EQ(socket_2_->message_history()[1], kDefaultMessageSize * 2 + 1);
+  EXPECT_EQ(socket_2_->message_history()[2], kDefaultMessageSize - 5);
+}
+
+TEST_F(SmallMessageSocketTest, BufferSendAndReceive) {
+  socket_1_->UseBufferPool();
+  socket_2_->UseBufferPool();
+  auto buffer = base::MakeRefCounted<net::IOBuffer>(kDefaultMessageSize +
+                                                    sizeof(uint16_t));
+  base::WriteBigEndian(buffer->data(),
+                       static_cast<uint16_t>(kDefaultMessageSize));
+  SetData(buffer->data() + sizeof(uint16_t), kDefaultMessageSize);
+  socket_2_->ReceiveMessages();
+  socket_1_->SendBuffer(std::move(buffer),
+                        kDefaultMessageSize + sizeof(uint16_t));
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(socket_2_->last_message_size(), kDefaultMessageSize);
+  EXPECT_GT(socket_2_->buffer_pool()->NumAllocatedForTesting(), 0u);
+  EXPECT_GT(socket_2_->buffer_pool()->NumFreeForTesting(), 0u);
+}
+
+TEST_F(SmallMessageSocketTest, SendLargerThanPoolBufferSize) {
+  socket_1_->UseBufferPool();
+  socket_2_->UseBufferPool();
+  char* buffer =
+      static_cast<char*>(socket_1_->PrepareSend(kDefaultMessageSize * 2));
+  SetData(buffer, kDefaultMessageSize * 2);
+  socket_2_->ReceiveMessages();
+  socket_1_->Send();
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(socket_2_->last_message_size(), kDefaultMessageSize * 2);
+}
+
+TEST_F(SmallMessageSocketTest, BufferMultipleMessages) {
+  socket_1_->UseBufferPool();
+  socket_2_->UseBufferPool();
+  char* buffer =
+      static_cast<char*>(socket_1_->PrepareSend(kDefaultMessageSize - 1));
+  SetData(buffer, kDefaultMessageSize - 1);
+  socket_1_->Send();
+  task_environment_.RunUntilIdle();
+
+  buffer =
+      static_cast<char*>(socket_1_->PrepareSend(kDefaultMessageSize * 2 + 1));
+  SetData(buffer, kDefaultMessageSize * 2 + 1);
+  socket_1_->Send();
+  task_environment_.RunUntilIdle();
+
+  buffer = static_cast<char*>(socket_1_->PrepareSend(kDefaultMessageSize - 5));
+  SetData(buffer, kDefaultMessageSize - 5);
+  socket_1_->Send();
+  task_environment_.RunUntilIdle();
+
+  buffer = static_cast<char*>(socket_1_->PrepareSend(kDefaultMessageSize));
+  SetData(buffer, kDefaultMessageSize);
+  socket_1_->Send();
+  task_environment_.RunUntilIdle();
+
+  socket_2_->ReceiveMessages();
+  task_environment_.RunUntilIdle();
+
+  ASSERT_EQ(socket_2_->message_history().size(), 4u);
+  EXPECT_EQ(socket_2_->message_history()[0], kDefaultMessageSize - 1);
+  EXPECT_EQ(socket_2_->message_history()[1], kDefaultMessageSize * 2 + 1);
+  EXPECT_EQ(socket_2_->message_history()[2], kDefaultMessageSize - 5);
+  EXPECT_EQ(socket_2_->message_history()[3], kDefaultMessageSize);
+}
+
+TEST_F(SmallMessageSocketTest, SwapPoolUse) {
+  socket_2_->SwapPoolUse(true);
+  char* buffer =
+      static_cast<char*>(socket_1_->PrepareSend(kDefaultMessageSize * 2 + 1));
+  SetData(buffer, kDefaultMessageSize * 2 + 1);
+  socket_1_->Send();
+  task_environment_.RunUntilIdle();
+
+  buffer = static_cast<char*>(socket_1_->PrepareSend(kDefaultMessageSize - 5));
+  SetData(buffer, kDefaultMessageSize - 5);
+  socket_1_->Send();
+  task_environment_.RunUntilIdle();
+
+  buffer = static_cast<char*>(socket_1_->PrepareSend(kDefaultMessageSize));
+  SetData(buffer, kDefaultMessageSize);
+  socket_1_->Send();
+  task_environment_.RunUntilIdle();
+
+  socket_2_->ReceiveMessages();
+  task_environment_.RunUntilIdle();
+
+  ASSERT_EQ(socket_2_->message_history().size(), 3u);
+  EXPECT_EQ(socket_2_->message_history()[0], kDefaultMessageSize * 2 + 1);
+  EXPECT_EQ(socket_2_->message_history()[1], kDefaultMessageSize - 5);
+  EXPECT_EQ(socket_2_->message_history()[2], kDefaultMessageSize);
+}
+
+}  // namespace chromecast
diff --git a/chromeos/services/multidevice_setup/account_status_change_delegate_notifier.cc b/chromeos/services/multidevice_setup/account_status_change_delegate_notifier.cc
index 2b2881f..1a65cef 100644
--- a/chromeos/services/multidevice_setup/account_status_change_delegate_notifier.cc
+++ b/chromeos/services/multidevice_setup/account_status_change_delegate_notifier.cc
@@ -17,16 +17,16 @@
 AccountStatusChangeDelegateNotifier::~AccountStatusChangeDelegateNotifier() =
     default;
 
-void AccountStatusChangeDelegateNotifier::SetAccountStatusChangeDelegatePtr(
-    mojom::AccountStatusChangeDelegatePtr delegate_ptr) {
-  if (delegate_ptr_.is_bound()) {
+void AccountStatusChangeDelegateNotifier::SetAccountStatusChangeDelegateRemote(
+    mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate_remote) {
+  if (delegate_remote_.is_bound()) {
     PA_LOG(ERROR) << "AccountStatusChangeDelegateNotifier::"
-                  << "SetAccountStatusChangeDelegatePtr(): Tried to set "
+                  << "SetAccountStatusChangeDelegateRemote(): Tried to set "
                   << "delegate, but one already existed.";
     NOTREACHED();
   }
 
-  delegate_ptr_ = std::move(delegate_ptr);
+  delegate_remote_.Bind(std::move(delegate_remote));
   OnDelegateSet();
 }
 
@@ -34,8 +34,8 @@
 void AccountStatusChangeDelegateNotifier::OnDelegateSet() {}
 
 void AccountStatusChangeDelegateNotifier::FlushForTesting() {
-  if (delegate_ptr_)
-    delegate_ptr_.FlushForTesting();
+  if (delegate_remote_)
+    delegate_remote_.FlushForTesting();
 }
 
 }  // namespace multidevice_setup
diff --git a/chromeos/services/multidevice_setup/account_status_change_delegate_notifier.h b/chromeos/services/multidevice_setup/account_status_change_delegate_notifier.h
index 4509173e9..9d988342 100644
--- a/chromeos/services/multidevice_setup/account_status_change_delegate_notifier.h
+++ b/chromeos/services/multidevice_setup/account_status_change_delegate_notifier.h
@@ -7,6 +7,8 @@
 
 #include "base/macros.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace chromeos {
 
@@ -21,17 +23,19 @@
  public:
   virtual ~AccountStatusChangeDelegateNotifier();
 
-  void SetAccountStatusChangeDelegatePtr(
-      mojom::AccountStatusChangeDelegatePtr delegate_ptr);
+  void SetAccountStatusChangeDelegateRemote(
+      mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate_remote);
 
  protected:
   AccountStatusChangeDelegateNotifier();
 
   // Derived classes should override this function to be alerted when
-  // SetAccountStatusChangeDelegatePtr() is called.
+  // SetAccountStatusChangeDelegateRemote() is called.
   virtual void OnDelegateSet();
 
-  mojom::AccountStatusChangeDelegate* delegate() { return delegate_ptr_.get(); }
+  mojom::AccountStatusChangeDelegate* delegate() {
+    return delegate_remote_.is_bound() ? delegate_remote_.get() : nullptr;
+  }
 
  private:
   friend class MultiDeviceSetupImpl;
@@ -40,7 +44,7 @@
 
   void FlushForTesting();
 
-  mojom::AccountStatusChangeDelegatePtr delegate_ptr_;
+  mojo::Remote<mojom::AccountStatusChangeDelegate> delegate_remote_;
 
   DISALLOW_COPY_AND_ASSIGN(AccountStatusChangeDelegateNotifier);
 };
diff --git a/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h b/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h
index d4722cb..6cbde22 100644
--- a/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h
+++ b/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h
@@ -13,6 +13,8 @@
 #include "chromeos/services/multidevice_setup/host_status_provider.h"
 #include "chromeos/services/multidevice_setup/public/cpp/oobe_completion_tracker.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 class PrefRegistrySimple;
 class PrefService;
@@ -55,8 +57,8 @@
 
   ~AccountStatusChangeDelegateNotifierImpl() override;
 
-  void SetAccountStatusChangeDelegatePtr(
-      mojom::AccountStatusChangeDelegatePtr delegate_ptr);
+  void SetAccountStatusChangeDelegateRemote(
+      mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate_remote);
 
  private:
   friend class MultiDeviceSetupAccountStatusChangeDelegateNotifierTest;
@@ -111,7 +113,7 @@
   // Set to base::nullopt until the first host status update.
   base::Optional<mojom::HostStatus> host_status_from_most_recent_update_;
 
-  mojom::AccountStatusChangeDelegatePtr delegate_ptr_;
+  mojo::Remote<mojom::AccountStatusChangeDelegate> delegate_remote_;
   HostStatusProvider* host_status_provider_;
   PrefService* pref_service_;
   HostDeviceTimestampManager* host_device_timestamp_manager_;
diff --git a/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc b/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc
index f5aef9a..2296938 100644
--- a/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc
+++ b/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc
@@ -142,9 +142,9 @@
         old_host_device_id);
   }
 
-  void SetAccountStatusChangeDelegatePtr() {
-    delegate_notifier_->SetAccountStatusChangeDelegatePtr(
-        fake_delegate_->GenerateInterfacePtr());
+  void SetAccountStatusChangeDelegateRemote() {
+    delegate_notifier_->SetAccountStatusChangeDelegateRemote(
+        fake_delegate_->GenerateRemote());
     delegate_notifier_->FlushForTesting();
   }
 
@@ -207,7 +207,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        SetObserverWithPotentialHost) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   EXPECT_EQ(0u, fake_delegate()->num_new_user_potential_host_events_handled());
   EXPECT_EQ(0u, GetNewUserPotentialHostExistsTimestamp());
 
@@ -220,7 +220,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        PotentialHostAddedLater) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   SetHostWithStatus(mojom::HostStatus::kNoEligibleHosts,
                     base::nullopt /* host_device */);
 
@@ -236,7 +236,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        OnlyPotentialHostCausesNewUserEvent) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
 
   SetHostWithStatus(mojom::HostStatus::kNoEligibleHosts,
                     base::nullopt /* host_device */);
@@ -276,7 +276,7 @@
   BuildAccountStatusChangeDelegateNotifier();
   int64_t earlier_test_time_millis = kTestTimeMillis / 2;
   SetNewUserPotentialHostExistsTimestamp(earlier_test_time_millis);
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
 
   SetHostWithStatus(mojom::HostStatus::kEligibleHostExistsButNoHostSet,
                     base::nullopt /* host_device */);
@@ -290,7 +290,7 @@
   BuildAccountStatusChangeDelegateNotifier();
   int64_t earlier_test_time_millis = kTestTimeMillis / 2;
   SetExistingUserChromebookAddedTimestamp(earlier_test_time_millis);
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
 
   SetHostWithStatus(mojom::HostStatus::kEligibleHostExistsButNoHostSet,
                     base::nullopt /* host_device */);
@@ -302,7 +302,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        LosingPotentialHostTriggersNoLongerNewUserEvent) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   SetHostWithStatus(mojom::HostStatus::kEligibleHostExistsButNoHostSet,
                     base::nullopt /* host_device */);
   EXPECT_EQ(1u, fake_delegate()->num_new_user_potential_host_events_handled());
@@ -318,7 +318,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        SettingHostTriggersNoLongerNewUserEvent) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   SetHostWithStatus(mojom::HostStatus::kEligibleHostExistsButNoHostSet,
                     base::nullopt /* host_device */);
   EXPECT_EQ(1u, fake_delegate()->num_new_user_potential_host_events_handled());
@@ -342,7 +342,7 @@
   EXPECT_EQ(kTestTimeMillis, GetOobeSetupFlowTimestamp());
 
   // Set delegate, which triggers event check.
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
 
   EXPECT_EQ(0u, fake_delegate()->num_new_user_potential_host_events_handled());
   EXPECT_EQ(0u, GetNewUserPotentialHostExistsTimestamp());
@@ -351,7 +351,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        CompletingOobeSetupFlowWithDelegateSetTriggersNoLongerNewUserEvent) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   SetHostWithStatus(mojom::HostStatus::kEligibleHostExistsButNoHostSet,
                     base::nullopt /* host_device */);
 
@@ -367,7 +367,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        NoLongerNewUserEventBlockedByOldChromebookAddedTimestamp) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
 
   // Record earlier Chromebook added event.
   int64_t earlier_test_time_millis = kTestTimeMillis / 2;
@@ -391,7 +391,7 @@
       BuildFakePhone(kFakePhoneKey, kFakePhoneName);
 
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   // Check the delegate initializes to 0.
   EXPECT_EQ(0u,
             fake_delegate()->num_existing_user_host_switched_events_handled());
@@ -423,7 +423,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        SettingSameHostTriggersNoHostSwitchedEvent) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   // Set initially verified host.
   SetHostWithStatus(mojom::HostStatus::kHostVerified,
                     BuildFakePhone(kFakePhoneKey, kFakePhoneName));
@@ -437,7 +437,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        ChangingHostDevicesTriggersHostSwitchEventWhenHostNameIsUnchanged) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   // Set initially verified host.
   SetHostWithStatus(mojom::HostStatus::kHostVerified,
                     BuildFakePhone(kFakePhoneKey, kFakePhoneName));
@@ -454,7 +454,7 @@
       BuildFakePhone(kFakePhoneKey, kFakePhoneName);
 
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   // Set initial host but do not verify.
   SetHostWithStatus(mojom::HostStatus::kHostSetButNotYetVerified, fakePhone);
   // Verify host.
@@ -466,7 +466,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        OnlyVerifiedHostCausesHostSwitchedEvent) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   // Set initially verified host.
   SetHostWithStatus(mojom::HostStatus::kHostVerified,
                     BuildFakePhone(kFakePhoneKey, kFakePhoneName));
@@ -492,7 +492,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        ForgettingAndThenSwitchingHostsDoesNotTriggerHostSwitchedEvent) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   // Set initially verified host.
   SetHostWithStatus(mojom::HostStatus::kHostVerified,
                     BuildFakePhone(kFakePhoneKey, kFakePhoneName));
@@ -520,7 +520,7 @@
   // Host switched and verified between sessions.
   SetHostWithStatus(mojom::HostStatus::kHostVerified,
                     BuildFakePhone(kFakePhoneKey, kFakePhoneName));
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   EXPECT_EQ(1u,
             fake_delegate()->num_existing_user_host_switched_events_handled());
 }
@@ -528,7 +528,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        NoHostSwitchedEventWithoutExistingHost) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   SetUpHost(BuildFakePhone(kFakePhoneKey, kFakePhoneName));
   EXPECT_EQ(0u,
             fake_delegate()->num_existing_user_host_switched_events_handled());
@@ -551,7 +551,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        NotifiesObserverForChromebookAddedEvents) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   // Check the delegate initializes to 0.
   EXPECT_EQ(
       0u, fake_delegate()->num_existing_user_chromebook_added_events_handled());
@@ -570,7 +570,7 @@
       BuildFakePhone(kFakePhoneKey, kFakePhoneName);
 
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   // Start with potential hosts but none set.
   SetHostWithStatus(mojom::HostStatus::kEligibleHostExistsButNoHostSet,
                     base::nullopt /* host_device */);
@@ -589,7 +589,7 @@
 TEST_F(MultiDeviceSetupAccountStatusChangeDelegateNotifierTest,
        ReplacingUnverifiedHostAWithVerifiedHostBCausesChromebookAddedEvent) {
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   // Start with potential hosts but none set.
   // Set initial host but do not verify.
   SetHostWithStatus(mojom::HostStatus::kHostSetButNotYetVerified,
@@ -613,7 +613,7 @@
   SetHostWithStatus(mojom::HostStatus::kHostVerified,
                     BuildFakePhone(kFakePhoneKey, kFakePhoneName));
 
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   EXPECT_EQ(
       1u, fake_delegate()->num_existing_user_chromebook_added_events_handled());
 }
@@ -624,7 +624,7 @@
       BuildFakePhone(kFakePhoneKey, kFakePhoneName);
 
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
 
   SetUpHost(fakePhone);
   // The host was set on this Chromebook so it should not trigger the Chromebook
@@ -648,7 +648,7 @@
             GetExistingUserChromebookAddedTimestamp());
 
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
 
   SetHostWithStatus(mojom::HostStatus::kHostVerified,
                     BuildFakePhone(kFakePhoneKey, kFakePhoneName));
@@ -677,7 +677,7 @@
       BuildFakePhone(kFakePhoneKeyA, kFakePhoneNameA);
 
   BuildAccountStatusChangeDelegateNotifier();
-  SetAccountStatusChangeDelegatePtr();
+  SetAccountStatusChangeDelegateRemote();
   // Check the delegate initializes to empty.
   EXPECT_EQ(GetMostRecentVerifiedHostDeviceIdPref(), "");
 
diff --git a/chromeos/services/multidevice_setup/fake_account_status_change_delegate.cc b/chromeos/services/multidevice_setup/fake_account_status_change_delegate.cc
index 928ed964..db9ebd49a 100644
--- a/chromeos/services/multidevice_setup/fake_account_status_change_delegate.cc
+++ b/chromeos/services/multidevice_setup/fake_account_status_change_delegate.cc
@@ -12,11 +12,11 @@
 
 FakeAccountStatusChangeDelegate::~FakeAccountStatusChangeDelegate() = default;
 
-mojom::AccountStatusChangeDelegatePtr
-FakeAccountStatusChangeDelegate::GenerateInterfacePtr() {
-  mojom::AccountStatusChangeDelegatePtr interface_ptr;
-  bindings_.AddBinding(this, mojo::MakeRequest(&interface_ptr));
-  return interface_ptr;
+mojo::PendingRemote<mojom::AccountStatusChangeDelegate>
+FakeAccountStatusChangeDelegate::GenerateRemote() {
+  mojo::PendingRemote<mojom::AccountStatusChangeDelegate> remote;
+  receivers_.Add(this, remote.InitWithNewPipeAndPassReceiver());
+  return remote;
 }
 
 void FakeAccountStatusChangeDelegate::OnPotentialHostExistsForNewUser() {
diff --git a/chromeos/services/multidevice_setup/fake_account_status_change_delegate.h b/chromeos/services/multidevice_setup/fake_account_status_change_delegate.h
index 1aae675..90d0514 100644
--- a/chromeos/services/multidevice_setup/fake_account_status_change_delegate.h
+++ b/chromeos/services/multidevice_setup/fake_account_status_change_delegate.h
@@ -7,7 +7,8 @@
 
 #include "base/macros.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 
 namespace chromeos {
 
@@ -20,7 +21,7 @@
   FakeAccountStatusChangeDelegate();
   ~FakeAccountStatusChangeDelegate() override;
 
-  mojom::AccountStatusChangeDelegatePtr GenerateInterfacePtr();
+  mojo::PendingRemote<mojom::AccountStatusChangeDelegate> GenerateRemote();
 
   size_t num_new_user_potential_host_events_handled() {
     return num_new_user_potential_host_events_handled_;
@@ -52,7 +53,7 @@
   size_t num_existing_user_host_switched_events_handled_ = 0u;
   size_t num_existing_user_chromebook_added_events_handled_ = 0u;
 
-  mojo::BindingSet<mojom::AccountStatusChangeDelegate> bindings_;
+  mojo::ReceiverSet<mojom::AccountStatusChangeDelegate> receivers_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeAccountStatusChangeDelegate);
 };
diff --git a/chromeos/services/multidevice_setup/fake_host_status_observer.cc b/chromeos/services/multidevice_setup/fake_host_status_observer.cc
index 49266695..247dbc2 100644
--- a/chromeos/services/multidevice_setup/fake_host_status_observer.cc
+++ b/chromeos/services/multidevice_setup/fake_host_status_observer.cc
@@ -12,10 +12,11 @@
 
 FakeHostStatusObserver::~FakeHostStatusObserver() = default;
 
-mojom::HostStatusObserverPtr FakeHostStatusObserver::GenerateInterfacePtr() {
-  mojom::HostStatusObserverPtr interface_ptr;
-  bindings_.AddBinding(this, mojo::MakeRequest(&interface_ptr));
-  return interface_ptr;
+mojo::PendingRemote<mojom::HostStatusObserver>
+FakeHostStatusObserver::GenerateRemote() {
+  mojo::PendingRemote<mojom::HostStatusObserver> remote;
+  receivers_.Add(this, remote.InitWithNewPipeAndPassReceiver());
+  return remote;
 }
 
 void FakeHostStatusObserver::OnHostStatusChanged(
diff --git a/chromeos/services/multidevice_setup/fake_host_status_observer.h b/chromeos/services/multidevice_setup/fake_host_status_observer.h
index 003876c4..71f3684 100644
--- a/chromeos/services/multidevice_setup/fake_host_status_observer.h
+++ b/chromeos/services/multidevice_setup/fake_host_status_observer.h
@@ -8,7 +8,8 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 
 namespace chromeos {
 
@@ -20,7 +21,7 @@
   FakeHostStatusObserver();
   ~FakeHostStatusObserver() override;
 
-  mojom::HostStatusObserverPtr GenerateInterfacePtr();
+  mojo::PendingRemote<mojom::HostStatusObserver> GenerateRemote();
 
   const std::vector<
       std::pair<mojom::HostStatus, base::Optional<multidevice::RemoteDevice>>>&
@@ -38,7 +39,7 @@
       std::pair<mojom::HostStatus, base::Optional<multidevice::RemoteDevice>>>
       host_status_updates_;
 
-  mojo::BindingSet<mojom::HostStatusObserver> bindings_;
+  mojo::ReceiverSet<mojom::HostStatusObserver> receivers_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeHostStatusObserver);
 };
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
index 1ba32fc..b7c3902 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
@@ -157,13 +157,13 @@
 }
 
 void MultiDeviceSetupImpl::SetAccountStatusChangeDelegate(
-    mojom::AccountStatusChangeDelegatePtr delegate) {
-  delegate_notifier_->SetAccountStatusChangeDelegatePtr(std::move(delegate));
+    mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate) {
+  delegate_notifier_->SetAccountStatusChangeDelegateRemote(std::move(delegate));
 }
 
 void MultiDeviceSetupImpl::AddHostStatusObserver(
-    mojom::HostStatusObserverPtr observer) {
-  host_status_observers_.AddPtr(std::move(observer));
+    mojo::PendingRemote<mojom::HostStatusObserver> observer) {
+  host_status_observers_.Add(std::move(observer));
 }
 
 void MultiDeviceSetupImpl::AddFeatureStateObserver(
@@ -276,7 +276,7 @@
 void MultiDeviceSetupImpl::TriggerEventForDebugging(
     mojom::EventTypeForDebugging type,
     TriggerEventForDebuggingCallback callback) {
-  if (!delegate_notifier_->delegate_ptr_) {
+  if (!delegate_notifier_->delegate_remote_) {
     PA_LOG(ERROR) << "MultiDeviceSetupImpl::TriggerEventForDebugging(): No "
                   << "delgate has been set; cannot proceed.";
     std::move(callback).Run(false /* success */);
@@ -286,7 +286,7 @@
   PA_LOG(VERBOSE) << "MultiDeviceSetupImpl::TriggerEventForDebugging(" << type
                   << ") called.";
   mojom::AccountStatusChangeDelegate* delegate =
-      delegate_notifier_->delegate_ptr_.get();
+      delegate_notifier_->delegate_remote_.get();
 
   switch (type) {
     case mojom::EventTypeForDebugging::kNewUserPotentialHostExists:
@@ -325,11 +325,8 @@
         host_status_with_device.host_device()->GetRemoteDevice();
   }
 
-  host_status_observers_.ForAllPtrs(
-      [&status_for_callback,
-       &device_for_callback](mojom::HostStatusObserver* observer) {
-        observer->OnHostStatusChanged(status_for_callback, device_for_callback);
-      });
+  for (auto& observer : host_status_observers_)
+    observer->OnHostStatusChanged(status_for_callback, device_for_callback);
 }
 
 void MultiDeviceSetupImpl::OnFeatureStatesChange(
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl.h b/chromeos/services/multidevice_setup/multidevice_setup_impl.h
index ce9662a..949cd1e0 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_impl.h
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl.h
@@ -14,6 +14,8 @@
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
 #include "url/gurl.h"
 
 class PrefService;
@@ -80,8 +82,10 @@
 
   // mojom::MultiDeviceSetup:
   void SetAccountStatusChangeDelegate(
-      mojom::AccountStatusChangeDelegatePtr delegate) override;
-  void AddHostStatusObserver(mojom::HostStatusObserverPtr observer) override;
+      mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate)
+      override;
+  void AddHostStatusObserver(
+      mojo::PendingRemote<mojom::HostStatusObserver> observer) override;
   void AddFeatureStateObserver(
       mojom::FeatureStateObserverPtr observer) override;
   void GetEligibleHostDevices(GetEligibleHostDevicesCallback callback) override;
@@ -136,7 +140,7 @@
       android_sms_app_installing_host_observer_;
   AuthTokenValidator* auth_token_validator_;
 
-  mojo::InterfacePtrSet<mojom::HostStatusObserver> host_status_observers_;
+  mojo::RemoteSet<mojom::HostStatusObserver> host_status_observers_;
   mojo::InterfacePtrSet<mojom::FeatureStateObserver> feature_state_observers_;
 
   DISALLOW_COPY_AND_ASSIGN(MultiDeviceSetupImpl);
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc b/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
index a940170..91a13c3 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
@@ -615,7 +615,7 @@
 
     EXPECT_FALSE(fake_account_status_change_delegate_notifier()->delegate());
     multidevice_setup_->SetAccountStatusChangeDelegate(
-        fake_account_status_change_delegate_->GenerateInterfacePtr());
+        fake_account_status_change_delegate_->GenerateRemote());
     EXPECT_TRUE(fake_account_status_change_delegate_notifier()->delegate());
   }
 
@@ -1111,7 +1111,7 @@
 
   // Add a status observer.
   auto observer = std::make_unique<FakeHostStatusObserver>();
-  multidevice_setup()->AddHostStatusObserver(observer->GenerateInterfacePtr());
+  multidevice_setup()->AddHostStatusObserver(observer->GenerateRemote());
 
   // Simulate a sync occurring; now, all of the test devices are eligible hosts.
   fake_eligible_host_devices_provider()->set_eligible_host_devices(
@@ -1202,7 +1202,7 @@
 TEST_F(MultiDeviceSetupImplTest, TestSetHostDeviceWithoutAuthToken) {
   // Add a status observer.
   auto observer = std::make_unique<FakeHostStatusObserver>();
-  multidevice_setup()->AddHostStatusObserver(observer->GenerateInterfacePtr());
+  multidevice_setup()->AddHostStatusObserver(observer->GenerateRemote());
 
   // Start valid eligible host devices.
   fake_eligible_host_devices_provider()->set_eligible_host_devices(
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc b/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
index 86e581d..f54219a0 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
@@ -98,7 +98,7 @@
 MultiDeviceSetupInitializer::~MultiDeviceSetupInitializer() = default;
 
 void MultiDeviceSetupInitializer::SetAccountStatusChangeDelegate(
-    mojom::AccountStatusChangeDelegatePtr delegate) {
+    mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate) {
   if (multidevice_setup_impl_) {
     multidevice_setup_impl_->SetAccountStatusChangeDelegate(
         std::move(delegate));
@@ -109,7 +109,7 @@
 }
 
 void MultiDeviceSetupInitializer::AddHostStatusObserver(
-    mojom::HostStatusObserverPtr observer) {
+    mojo::PendingRemote<mojom::HostStatusObserver> observer) {
   if (multidevice_setup_impl_) {
     multidevice_setup_impl_->AddHostStatusObserver(std::move(observer));
     return;
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_initializer.h b/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
index 92834cf6..47e84b0 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
+++ b/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
@@ -12,7 +12,7 @@
 #include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
 #include "chromeos/services/multidevice_setup/multidevice_setup_base.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 
 class PrefService;
 
@@ -88,8 +88,10 @@
 
   // mojom::MultiDeviceSetup:
   void SetAccountStatusChangeDelegate(
-      mojom::AccountStatusChangeDelegatePtr delegate) override;
-  void AddHostStatusObserver(mojom::HostStatusObserverPtr observer) override;
+      mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate)
+      override;
+  void AddHostStatusObserver(
+      mojo::PendingRemote<mojom::HostStatusObserver> observer) override;
   void AddFeatureStateObserver(
       mojom::FeatureStateObserverPtr observer) override;
   void GetEligibleHostDevices(GetEligibleHostDevicesCallback callback) override;
@@ -132,8 +134,9 @@
   // If API functions are called before initialization is complete, their
   // parameters are cached here. Once asynchronous initialization is complete,
   // the parameters are passed to |multidevice_setup_impl_|.
-  mojom::AccountStatusChangeDelegatePtr pending_delegate_;
-  std::vector<mojom::HostStatusObserverPtr> pending_host_status_observers_;
+  mojo::PendingRemote<mojom::AccountStatusChangeDelegate> pending_delegate_;
+  std::vector<mojo::PendingRemote<mojom::HostStatusObserver>>
+      pending_host_status_observers_;
   std::vector<mojom::FeatureStateObserverPtr> pending_feature_state_observers_;
   std::vector<GetEligibleHostDevicesCallback> pending_get_eligible_hosts_args_;
   std::vector<GetHostStatusCallback> pending_get_host_args_;
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc b/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
index a7a68ca..a573363 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
@@ -243,13 +243,13 @@
   auto fake_account_status_change_delegate =
       std::make_unique<FakeAccountStatusChangeDelegate>();
   multidevice_setup_ptr()->SetAccountStatusChangeDelegate(
-      fake_account_status_change_delegate->GenerateInterfacePtr());
+      fake_account_status_change_delegate->GenerateRemote());
   multidevice_setup_ptr().FlushForTesting();
 
   // AddHostStatusObserver().
   auto fake_host_status_observer = std::make_unique<FakeHostStatusObserver>();
   multidevice_setup_ptr()->AddHostStatusObserver(
-      fake_host_status_observer->GenerateInterfacePtr());
+      fake_host_status_observer->GenerateRemote());
   multidevice_setup_ptr().FlushForTesting();
 
   // AddFeatureStateObserver().
@@ -388,14 +388,14 @@
   auto fake_account_status_change_delegate =
       std::make_unique<FakeAccountStatusChangeDelegate>();
   multidevice_setup_ptr()->SetAccountStatusChangeDelegate(
-      fake_account_status_change_delegate->GenerateInterfacePtr());
+      fake_account_status_change_delegate->GenerateRemote());
   multidevice_setup_ptr().FlushForTesting();
   EXPECT_TRUE(fake_multidevice_setup()->delegate());
 
   // AddHostStatusObserver().
   auto fake_host_status_observer = std::make_unique<FakeHostStatusObserver>();
   multidevice_setup_ptr()->AddHostStatusObserver(
-      fake_host_status_observer->GenerateInterfacePtr());
+      fake_host_status_observer->GenerateRemote());
   multidevice_setup_ptr().FlushForTesting();
   EXPECT_TRUE(fake_multidevice_setup()->HasAtLeastOneHostStatusObserver());
 
diff --git a/chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc b/chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc
index ef80b0f9..098c271 100644
--- a/chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc
+++ b/chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc
@@ -82,10 +82,8 @@
 void FakeMultiDeviceSetup::NotifyHostStatusChanged(
     mojom::HostStatus host_status,
     const base::Optional<multidevice::RemoteDevice>& host_device) {
-  host_status_observers_.ForAllPtrs(
-      [&host_status, &host_device](mojom::HostStatusObserver* observer) {
-        observer->OnHostStatusChanged(host_status, host_device);
-      });
+  for (auto& observer : host_status_observers_)
+    observer->OnHostStatusChanged(host_status, host_device);
 }
 
 void FakeMultiDeviceSetup::NotifyFeatureStateChanged(
@@ -98,13 +96,13 @@
 }
 
 void FakeMultiDeviceSetup::SetAccountStatusChangeDelegate(
-    mojom::AccountStatusChangeDelegatePtr delegate) {
-  delegate_ = std::move(delegate);
+    mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate) {
+  delegate_.Bind(std::move(delegate));
 }
 
 void FakeMultiDeviceSetup::AddHostStatusObserver(
-    mojom::HostStatusObserverPtr observer) {
-  host_status_observers_.AddPtr(std::move(observer));
+    mojo::PendingRemote<mojom::HostStatusObserver> observer) {
+  host_status_observers_.Add(std::move(observer));
 }
 
 void FakeMultiDeviceSetup::AddFeatureStateObserver(
diff --git a/chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup.h b/chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup.h
index 111f2f4..0a1e9b25 100644
--- a/chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup.h
+++ b/chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup.h
@@ -13,6 +13,9 @@
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
 
 namespace chromeos {
 
@@ -37,7 +40,9 @@
       const base::flat_map<mojom::Feature, mojom::FeatureState>&
           feature_states);
 
-  mojom::AccountStatusChangeDelegatePtr& delegate() { return delegate_; }
+  mojo::Remote<mojom::AccountStatusChangeDelegate>& delegate() {
+    return delegate_;
+  }
 
   std::vector<GetEligibleHostDevicesCallback>& get_eligible_hosts_args() {
     return get_eligible_hosts_args_;
@@ -84,8 +89,10 @@
  private:
   // mojom::MultiDeviceSetup:
   void SetAccountStatusChangeDelegate(
-      mojom::AccountStatusChangeDelegatePtr delegate) override;
-  void AddHostStatusObserver(mojom::HostStatusObserverPtr observer) override;
+      mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate)
+      override;
+  void AddHostStatusObserver(
+      mojo::PendingRemote<mojom::HostStatusObserver> observer) override;
   void AddFeatureStateObserver(
       mojom::FeatureStateObserverPtr observer) override;
   void GetEligibleHostDevices(GetEligibleHostDevicesCallback callback) override;
@@ -110,8 +117,8 @@
       mojom::PrivilegedHostDeviceSetter::SetHostDeviceCallback callback)
       override;
 
-  mojom::AccountStatusChangeDelegatePtr delegate_;
-  mojo::InterfacePtrSet<mojom::HostStatusObserver> host_status_observers_;
+  mojo::Remote<mojom::AccountStatusChangeDelegate> delegate_;
+  mojo::RemoteSet<mojom::HostStatusObserver> host_status_observers_;
   mojo::InterfacePtrSet<mojom::FeatureStateObserver> feature_state_observers_;
 
   std::vector<GetEligibleHostDevicesCallback> get_eligible_hosts_args_;
diff --git a/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc b/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
index 65e5ca3..b4a7fe6 100644
--- a/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
+++ b/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
@@ -48,15 +48,14 @@
 
 MultiDeviceSetupClientImpl::MultiDeviceSetupClientImpl(
     service_manager::Connector* connector)
-    : host_status_observer_binding_(this),
-      feature_state_observer_binding_(this),
+    : feature_state_observer_binding_(this),
       remote_device_cache_(
           multidevice::RemoteDeviceCache::Factory::Get()->BuildInstance()),
       host_status_with_device_(GenerateDefaultHostStatusWithDevice()),
       feature_states_map_(GenerateDefaultFeatureStatesMap()) {
   connector->BindInterface(mojom::kServiceName, &multidevice_setup_ptr_);
   multidevice_setup_ptr_->AddHostStatusObserver(
-      GenerateHostStatusObserverInterfacePtr());
+      GenerateHostStatusObserverRemote());
   multidevice_setup_ptr_->AddFeatureStateObserver(
       GenerateFeatureStatesObserverInterfacePtr());
   multidevice_setup_ptr_->GetHostStatus(
@@ -156,11 +155,9 @@
   std::move(callback).Run(eligible_host_device_refs);
 }
 
-mojom::HostStatusObserverPtr
-MultiDeviceSetupClientImpl::GenerateHostStatusObserverInterfacePtr() {
-  mojom::HostStatusObserverPtr interface_ptr;
-  host_status_observer_binding_.Bind(mojo::MakeRequest(&interface_ptr));
-  return interface_ptr;
+mojo::PendingRemote<mojom::HostStatusObserver>
+MultiDeviceSetupClientImpl::GenerateHostStatusObserverRemote() {
+  return host_status_observer_receiver_.BindNewPipeAndPassRemote();
 }
 
 mojom::FeatureStateObserverPtr
diff --git a/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h b/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h
index 0c54a55c..d42e058 100644
--- a/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h
+++ b/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h
@@ -17,6 +17,8 @@
 #include "chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace service_manager {
 class Connector;
@@ -85,13 +87,15 @@
       GetEligibleHostDevicesCallback callback,
       const multidevice::RemoteDeviceList& eligible_host_devices);
 
-  mojom::HostStatusObserverPtr GenerateHostStatusObserverInterfacePtr();
+  mojo::PendingRemote<mojom::HostStatusObserver>
+  GenerateHostStatusObserverRemote();
   mojom::FeatureStateObserverPtr GenerateFeatureStatesObserverInterfacePtr();
 
   void FlushForTesting();
 
   mojom::MultiDeviceSetupPtr multidevice_setup_ptr_;
-  mojo::Binding<mojom::HostStatusObserver> host_status_observer_binding_;
+  mojo::Receiver<mojom::HostStatusObserver> host_status_observer_receiver_{
+      this};
   mojo::Binding<mojom::FeatureStateObserver> feature_state_observer_binding_;
   std::unique_ptr<multidevice::RemoteDeviceCache> remote_device_cache_;
 
diff --git a/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom b/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom
index 2295836..c74e6ec 100644
--- a/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom
+++ b/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom
@@ -138,11 +138,12 @@
   // Registers the "account status change" delegate to be used by the service.
   // Only one delegate can be set; this function should not be called more than
   // once.
-  SetAccountStatusChangeDelegate(AccountStatusChangeDelegate delegate);
+  SetAccountStatusChangeDelegate(
+      pending_remote<AccountStatusChangeDelegate> delegate);
 
   // Adds an observer of host status changes. To stop observing, disconnect the
-  // HostStatusObserverPtr passed here.
-  AddHostStatusObserver(HostStatusObserver observer);
+  // pending_remote<HostStatusObserver> passed here.
+  AddHostStatusObserver(pending_remote<HostStatusObserver> observer);
 
   // Adds an observer of feature state changes. To stop observing, disconnect
   // the FeatureStateObserverPtr passed here.
diff --git a/chromeos/services/secure_channel/client_connection_parameters_impl.cc b/chromeos/services/secure_channel/client_connection_parameters_impl.cc
index 8c16f94..ac82e281 100644
--- a/chromeos/services/secure_channel/client_connection_parameters_impl.cc
+++ b/chromeos/services/secure_channel/client_connection_parameters_impl.cc
@@ -37,42 +37,42 @@
 std::unique_ptr<ClientConnectionParameters>
 ClientConnectionParametersImpl::Factory::BuildInstance(
     const std::string& feature,
-    mojom::ConnectionDelegatePtr connection_delegate_ptr) {
+    mojo::PendingRemote<mojom::ConnectionDelegate> connection_delegate_remote) {
   return base::WrapUnique(new ClientConnectionParametersImpl(
-      feature, std::move(connection_delegate_ptr)));
+      feature, std::move(connection_delegate_remote)));
 }
 
 ClientConnectionParametersImpl::ClientConnectionParametersImpl(
     const std::string& feature,
-    mojom::ConnectionDelegatePtr connection_delegate_ptr)
+    mojo::PendingRemote<mojom::ConnectionDelegate> connection_delegate_remote)
     : ClientConnectionParameters(feature),
-      connection_delegate_ptr_(std::move(connection_delegate_ptr)) {
+      connection_delegate_remote_(std::move(connection_delegate_remote)) {
   // If the client disconnects its delegate, the client is signaling that the
   // connection request has been canceled.
-  connection_delegate_ptr_.set_connection_error_handler(base::BindOnce(
-      &ClientConnectionParametersImpl::OnConnectionDelegatePtrDisconnected,
+  connection_delegate_remote_.set_disconnect_handler(base::BindOnce(
+      &ClientConnectionParametersImpl::OnConnectionDelegateRemoteDisconnected,
       base::Unretained(this)));
 }
 
 ClientConnectionParametersImpl::~ClientConnectionParametersImpl() = default;
 
 bool ClientConnectionParametersImpl::HasClientCanceledRequest() {
-  return connection_delegate_ptr_.encountered_error();
+  return !connection_delegate_remote_.is_connected();
 }
 
 void ClientConnectionParametersImpl::PerformSetConnectionAttemptFailed(
     mojom::ConnectionAttemptFailureReason reason) {
-  connection_delegate_ptr_->OnConnectionAttemptFailure(reason);
+  connection_delegate_remote_->OnConnectionAttemptFailure(reason);
 }
 
 void ClientConnectionParametersImpl::PerformSetConnectionSucceeded(
     mojo::PendingRemote<mojom::Channel> channel,
     mojo::PendingReceiver<mojom::MessageReceiver> message_receiver_receiver) {
-  connection_delegate_ptr_->OnConnection(std::move(channel),
-                                         std::move(message_receiver_receiver));
+  connection_delegate_remote_->OnConnection(
+      std::move(channel), std::move(message_receiver_receiver));
 }
 
-void ClientConnectionParametersImpl::OnConnectionDelegatePtrDisconnected() {
+void ClientConnectionParametersImpl::OnConnectionDelegateRemoteDisconnected() {
   NotifyConnectionRequestCanceled();
 }
 
diff --git a/chromeos/services/secure_channel/client_connection_parameters_impl.h b/chromeos/services/secure_channel/client_connection_parameters_impl.h
index d1d45b8..02afce0 100644
--- a/chromeos/services/secure_channel/client_connection_parameters_impl.h
+++ b/chromeos/services/secure_channel/client_connection_parameters_impl.h
@@ -10,13 +10,14 @@
 #include "chromeos/services/secure_channel/public/mojom/secure_channel.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace chromeos {
 
 namespace secure_channel {
 
 // Concrete ClientConnectionParameters implementation, which utilizes a
-// ConnectionDelegatePtr.
+// mojo::Remote<ConnectionDelegate>.
 class ClientConnectionParametersImpl : public ClientConnectionParameters {
  public:
   class Factory {
@@ -26,7 +27,8 @@
     virtual ~Factory();
     virtual std::unique_ptr<ClientConnectionParameters> BuildInstance(
         const std::string& feature,
-        mojom::ConnectionDelegatePtr connection_delegate_ptr);
+        mojo::PendingRemote<mojom::ConnectionDelegate>
+            connection_delegate_remote);
 
    private:
     static Factory* test_factory_;
@@ -35,9 +37,9 @@
   ~ClientConnectionParametersImpl() override;
 
  private:
-  ClientConnectionParametersImpl(
-      const std::string& feature,
-      mojom::ConnectionDelegatePtr connection_delegate_ptr);
+  ClientConnectionParametersImpl(const std::string& feature,
+                                 mojo::PendingRemote<mojom::ConnectionDelegate>
+                                     connection_delegate_remote);
 
   // ClientConnectionParameters:
   bool HasClientCanceledRequest() override;
@@ -48,9 +50,9 @@
       mojo::PendingReceiver<mojom::MessageReceiver> message_receiver_receiver)
       override;
 
-  void OnConnectionDelegatePtrDisconnected();
+  void OnConnectionDelegateRemoteDisconnected();
 
-  mojom::ConnectionDelegatePtr connection_delegate_ptr_;
+  mojo::Remote<mojom::ConnectionDelegate> connection_delegate_remote_;
 
   DISALLOW_COPY_AND_ASSIGN(ClientConnectionParametersImpl);
 };
diff --git a/chromeos/services/secure_channel/client_connection_parameters_impl_unittest.cc b/chromeos/services/secure_channel/client_connection_parameters_impl_unittest.cc
index 49ae4a1..09de4e5 100644
--- a/chromeos/services/secure_channel/client_connection_parameters_impl_unittest.cc
+++ b/chromeos/services/secure_channel/client_connection_parameters_impl_unittest.cc
@@ -29,14 +29,12 @@
   // testing::Test:
   void SetUp() override {
     fake_connection_delegate_ = std::make_unique<FakeConnectionDelegate>();
-    auto fake_connection_delegate_interface_ptr =
-        fake_connection_delegate_->GenerateInterfacePtr();
-    fake_connection_delegate_proxy_ =
-        fake_connection_delegate_interface_ptr.get();
+    auto fake_connection_delegate_remote =
+        fake_connection_delegate_->GenerateRemote();
 
     client_connection_parameters_ =
         ClientConnectionParametersImpl::Factory::Get()->BuildInstance(
-            kTestFeature, std::move(fake_connection_delegate_interface_ptr));
+            kTestFeature, std::move(fake_connection_delegate_remote));
 
     fake_observer_ = std::make_unique<FakeClientConnectionParametersObserver>();
     client_connection_parameters_->AddObserver(fake_observer_.get());
@@ -46,10 +44,10 @@
     client_connection_parameters_->RemoveObserver(fake_observer_.get());
   }
 
-  void DisconnectConnectionDelegatePtr() {
+  void DisconnectConnectionDelegateRemote() {
     base::RunLoop run_loop;
     fake_observer_->set_closure_for_next_callback(run_loop.QuitClosure());
-    fake_connection_delegate_->DisconnectGeneratedPtrs();
+    fake_connection_delegate_->DisconnectGeneratedRemotes();
     run_loop.Run();
   }
 
@@ -89,7 +87,6 @@
   base::test::TaskEnvironment task_environment_;
 
   std::unique_ptr<FakeConnectionDelegate> fake_connection_delegate_;
-  mojom::ConnectionDelegate::Proxy_* fake_connection_delegate_proxy_ = nullptr;
 
   std::unique_ptr<FakeClientConnectionParametersObserver> fake_observer_;
 
@@ -100,7 +97,7 @@
 
 TEST_F(SecureChannelClientConnectionParametersImplTest,
        ConnectionDelegateDisconnected) {
-  DisconnectConnectionDelegatePtr();
+  DisconnectConnectionDelegateRemote();
   VerifyStatus(false /* expected_to_be_waiting_for_response */,
                true /* expected_to_be_canceled */);
 }
diff --git a/chromeos/services/secure_channel/fake_connection_delegate.cc b/chromeos/services/secure_channel/fake_connection_delegate.cc
index 4dfb115..34462575 100644
--- a/chromeos/services/secure_channel/fake_connection_delegate.cc
+++ b/chromeos/services/secure_channel/fake_connection_delegate.cc
@@ -15,14 +15,15 @@
 
 FakeConnectionDelegate::~FakeConnectionDelegate() = default;
 
-mojom::ConnectionDelegatePtr FakeConnectionDelegate::GenerateInterfacePtr() {
-  mojom::ConnectionDelegatePtr interface_ptr;
-  bindings_.AddBinding(this, mojo::MakeRequest(&interface_ptr));
-  return interface_ptr;
+mojo::PendingRemote<mojom::ConnectionDelegate>
+FakeConnectionDelegate::GenerateRemote() {
+  mojo::PendingRemote<mojom::ConnectionDelegate> remote;
+  receivers_.Add(this, remote.InitWithNewPipeAndPassReceiver());
+  return remote;
 }
 
-void FakeConnectionDelegate::DisconnectGeneratedPtrs() {
-  bindings_.CloseAllBindings();
+void FakeConnectionDelegate::DisconnectGeneratedRemotes() {
+  receivers_.Clear();
 }
 
 void FakeConnectionDelegate::OnConnectionAttemptFailure(
diff --git a/chromeos/services/secure_channel/fake_connection_delegate.h b/chromeos/services/secure_channel/fake_connection_delegate.h
index 05a012e..b5599a0 100644
--- a/chromeos/services/secure_channel/fake_connection_delegate.h
+++ b/chromeos/services/secure_channel/fake_connection_delegate.h
@@ -7,9 +7,9 @@
 
 #include "base/macros.h"
 #include "chromeos/services/secure_channel/public/mojom/secure_channel.mojom.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace chromeos {
@@ -22,8 +22,8 @@
   FakeConnectionDelegate();
   ~FakeConnectionDelegate() override;
 
-  mojom::ConnectionDelegatePtr GenerateInterfacePtr();
-  void DisconnectGeneratedPtrs();
+  mojo::PendingRemote<mojom::ConnectionDelegate> GenerateRemote();
+  void DisconnectGeneratedRemotes();
 
   const base::Optional<mojom::ConnectionAttemptFailureReason>&
   connection_attempt_failure_reason() const {
@@ -52,7 +52,7 @@
   void OnChannelDisconnected(uint32_t disconnection_reason,
                              const std::string& disconnection_description);
 
-  mojo::BindingSet<mojom::ConnectionDelegate> bindings_;
+  mojo::ReceiverSet<mojom::ConnectionDelegate> receivers_;
   base::OnceClosure closure_for_next_delegate_callback_;
 
   base::Optional<mojom::ConnectionAttemptFailureReason>
diff --git a/chromeos/services/secure_channel/fake_secure_channel.cc b/chromeos/services/secure_channel/fake_secure_channel.cc
index 6fc7db23..dc7bad7 100644
--- a/chromeos/services/secure_channel/fake_secure_channel.cc
+++ b/chromeos/services/secure_channel/fake_secure_channel.cc
@@ -19,8 +19,8 @@
     const multidevice::RemoteDevice& local_device,
     const std::string& feature,
     ConnectionPriority connection_priority,
-    mojom::ConnectionDelegatePtr delegate) {
-  delegate_from_last_listen_call_ = std::move(delegate);
+    mojo::PendingRemote<mojom::ConnectionDelegate> delegate) {
+  delegate_from_last_listen_call_.Bind(std::move(delegate));
 }
 
 void FakeSecureChannel::InitiateConnectionToDevice(
@@ -28,8 +28,8 @@
     const multidevice::RemoteDevice& local_device,
     const std::string& feature,
     ConnectionPriority connection_priority,
-    mojom::ConnectionDelegatePtr delegate) {
-  delegate_from_last_initiate_call_ = std::move(delegate);
+    mojo::PendingRemote<mojom::ConnectionDelegate> delegate) {
+  delegate_from_last_initiate_call_.Bind(std::move(delegate));
 }
 
 }  // namespace secure_channel
diff --git a/chromeos/services/secure_channel/fake_secure_channel.h b/chromeos/services/secure_channel/fake_secure_channel.h
index c851b65..4dba84f 100644
--- a/chromeos/services/secure_channel/fake_secure_channel.h
+++ b/chromeos/services/secure_channel/fake_secure_channel.h
@@ -14,6 +14,8 @@
 #include "chromeos/components/multidevice/remote_device_cache.h"
 #include "chromeos/services/secure_channel/public/mojom/secure_channel.mojom.h"
 #include "chromeos/services/secure_channel/secure_channel_base.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace chromeos {
 
@@ -25,11 +27,11 @@
   FakeSecureChannel();
   ~FakeSecureChannel() override;
 
-  mojom::ConnectionDelegatePtr delegate_from_last_listen_call() {
+  mojo::Remote<mojom::ConnectionDelegate> delegate_from_last_listen_call() {
     return std::move(delegate_from_last_listen_call_);
   }
 
-  mojom::ConnectionDelegatePtr delegate_from_last_initiate_call() {
+  mojo::Remote<mojom::ConnectionDelegate> delegate_from_last_initiate_call() {
     return std::move(delegate_from_last_initiate_call_);
   }
 
@@ -40,16 +42,16 @@
       const multidevice::RemoteDevice& local_device,
       const std::string& feature,
       ConnectionPriority connection_priority,
-      mojom::ConnectionDelegatePtr delegate) override;
+      mojo::PendingRemote<mojom::ConnectionDelegate> delegate) override;
   void InitiateConnectionToDevice(
       const multidevice::RemoteDevice& device_to_connect,
       const multidevice::RemoteDevice& local_device,
       const std::string& feature,
       ConnectionPriority connection_priority,
-      mojom::ConnectionDelegatePtr delegate) override;
+      mojo::PendingRemote<mojom::ConnectionDelegate> delegate) override;
 
-  mojom::ConnectionDelegatePtr delegate_from_last_listen_call_;
-  mojom::ConnectionDelegatePtr delegate_from_last_initiate_call_;
+  mojo::Remote<mojom::ConnectionDelegate> delegate_from_last_listen_call_;
+  mojo::Remote<mojom::ConnectionDelegate> delegate_from_last_initiate_call_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeSecureChannel);
 };
diff --git a/chromeos/services/secure_channel/pending_connection_request_base.h b/chromeos/services/secure_channel/pending_connection_request_base.h
index 923fcde7..4bc36ec 100644
--- a/chromeos/services/secure_channel/pending_connection_request_base.h
+++ b/chromeos/services/secure_channel/pending_connection_request_base.h
@@ -23,9 +23,10 @@
 // Encapsulates metadata for a pending request for a connection to a remote
 // device. Every PendingConnectionRequestBase starts out active (i.e., there
 // exists an ongoing attempt to create a connection). The client of this class
-// can cancel an active attempt by disconnecting the ConnectionDelegatePtr
-// passed PendingConnectionRequestBase's constructor; likewise, a
-// PendingConnectionRequestBase can become inactive due to connection failures.
+// can cancel an active attempt by disconnecting the
+// mojo::Remote<ConnectionDelegate> passed PendingConnectionRequestBase's
+// constructor; likewise, a PendingConnectionRequestBase can become inactive due
+// to connection failures.
 //
 // Each connection type should implement its own pending request class deriving
 // from PendingConnectionRequestBase.
diff --git a/chromeos/services/secure_channel/public/cpp/client/connection_attempt_impl.cc b/chromeos/services/secure_channel/public/cpp/client/connection_attempt_impl.cc
index 3f7af3c7..5faba75 100644
--- a/chromeos/services/secure_channel/public/cpp/client/connection_attempt_impl.cc
+++ b/chromeos/services/secure_channel/public/cpp/client/connection_attempt_impl.cc
@@ -38,14 +38,13 @@
   return base::WrapUnique(new ConnectionAttemptImpl());
 }
 
-ConnectionAttemptImpl::ConnectionAttemptImpl() : binding_(this) {}
+ConnectionAttemptImpl::ConnectionAttemptImpl() = default;
 
 ConnectionAttemptImpl::~ConnectionAttemptImpl() = default;
 
-mojom::ConnectionDelegatePtr ConnectionAttemptImpl::GenerateInterfacePtr() {
-  mojom::ConnectionDelegatePtr interface_ptr;
-  binding_.Bind(mojo::MakeRequest(&interface_ptr));
-  return interface_ptr;
+mojo::PendingRemote<mojom::ConnectionDelegate>
+ConnectionAttemptImpl::GenerateRemote() {
+  return receiver_.BindNewPipeAndPassRemote();
 }
 
 void ConnectionAttemptImpl::OnConnectionAttemptFailure(
diff --git a/chromeos/services/secure_channel/public/cpp/client/connection_attempt_impl.h b/chromeos/services/secure_channel/public/cpp/client/connection_attempt_impl.h
index bc55b30..85e53d24 100644
--- a/chromeos/services/secure_channel/public/cpp/client/connection_attempt_impl.h
+++ b/chromeos/services/secure_channel/public/cpp/client/connection_attempt_impl.h
@@ -8,9 +8,9 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/services/secure_channel/public/cpp/client/connection_attempt.h"
-#include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace chromeos {
 
@@ -33,7 +33,7 @@
 
   ~ConnectionAttemptImpl() override;
 
-  mojom::ConnectionDelegatePtr GenerateInterfacePtr();
+  mojo::PendingRemote<mojom::ConnectionDelegate> GenerateRemote();
 
  protected:
   ConnectionAttemptImpl();
@@ -46,7 +46,7 @@
                         message_receiver_receiver) override;
 
  private:
-  mojo::Binding<mojom::ConnectionDelegate> binding_;
+  mojo::Receiver<mojom::ConnectionDelegate> receiver_{this};
 
   base::WeakPtrFactory<ConnectionAttemptImpl> weak_ptr_factory_{this};
 
diff --git a/chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl.cc b/chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl.cc
index bdda789..38b89ef 100644
--- a/chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl.cc
+++ b/chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl.cc
@@ -45,7 +45,7 @@
 SecureChannelClientImpl::SecureChannelClientImpl(
     mojo::PendingRemote<mojom::SecureChannel> channel,
     scoped_refptr<base::TaskRunner> task_runner)
-    : secure_channel_ptr_(std::move(channel)), task_runner_(task_runner) {}
+    : secure_channel_remote_(std::move(channel)), task_runner_(task_runner) {}
 
 SecureChannelClientImpl::~SecureChannelClientImpl() = default;
 
@@ -65,8 +65,7 @@
       base::BindOnce(
           &SecureChannelClientImpl::PerformInitiateConnectionToDevice,
           weak_ptr_factory_.GetWeakPtr(), device_to_connect, local_device,
-          feature, connection_priority,
-          connection_attempt->GenerateInterfacePtr()));
+          feature, connection_priority, connection_attempt->GenerateRemote()));
 
   return connection_attempt;
 }
@@ -88,8 +87,7 @@
       base::BindOnce(
           &SecureChannelClientImpl::PerformListenForConnectionFromDevice,
           weak_ptr_factory_.GetWeakPtr(), device_to_connect, local_device,
-          feature, connection_priority,
-          connection_attempt->GenerateInterfacePtr()));
+          feature, connection_priority, connection_attempt->GenerateRemote()));
 
   return connection_attempt;
 }
@@ -99,10 +97,10 @@
     multidevice::RemoteDeviceRef local_device,
     const std::string& feature,
     ConnectionPriority connection_priority,
-    mojom::ConnectionDelegatePtr connection_delegate_ptr) {
-  secure_channel_ptr_->InitiateConnectionToDevice(
+    mojo::PendingRemote<mojom::ConnectionDelegate> connection_delegate_remote) {
+  secure_channel_remote_->InitiateConnectionToDevice(
       device_to_connect.GetRemoteDevice(), local_device.GetRemoteDevice(),
-      feature, connection_priority, std::move(connection_delegate_ptr));
+      feature, connection_priority, std::move(connection_delegate_remote));
 }
 
 void SecureChannelClientImpl::PerformListenForConnectionFromDevice(
@@ -110,14 +108,14 @@
     multidevice::RemoteDeviceRef local_device,
     const std::string& feature,
     ConnectionPriority connection_priority,
-    mojom::ConnectionDelegatePtr connection_delegate_ptr) {
-  secure_channel_ptr_->ListenForConnectionFromDevice(
+    mojo::PendingRemote<mojom::ConnectionDelegate> connection_delegate_remote) {
+  secure_channel_remote_->ListenForConnectionFromDevice(
       device_to_connect.GetRemoteDevice(), local_device.GetRemoteDevice(),
-      feature, connection_priority, std::move(connection_delegate_ptr));
+      feature, connection_priority, std::move(connection_delegate_remote));
 }
 
 void SecureChannelClientImpl::FlushForTesting() {
-  secure_channel_ptr_.FlushForTesting();
+  secure_channel_remote_.FlushForTesting();
 }
 
 }  // namespace secure_channel
diff --git a/chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl.h b/chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl.h
index a7f7b2d..5933828 100644
--- a/chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl.h
+++ b/chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl.h
@@ -8,8 +8,8 @@
 #include "base/memory/weak_ptr.h"
 #include "chromeos/services/secure_channel/public/cpp/client/secure_channel_client.h"
 #include "chromeos/services/secure_channel/public/mojom/secure_channel.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace base {
 class TaskRunner;
@@ -61,17 +61,19 @@
       multidevice::RemoteDeviceRef local_device,
       const std::string& feature,
       ConnectionPriority connection_priority,
-      mojom::ConnectionDelegatePtr connection_delegate_ptr);
+      mojo::PendingRemote<mojom::ConnectionDelegate>
+          connection_delegate_remote);
   void PerformListenForConnectionFromDevice(
       multidevice::RemoteDeviceRef device_to_connect,
       multidevice::RemoteDeviceRef local_device,
       const std::string& feature,
       ConnectionPriority connection_priority,
-      mojom::ConnectionDelegatePtr connection_delegate_ptr);
+      mojo::PendingRemote<mojom::ConnectionDelegate>
+          connection_delegate_remote);
 
   void FlushForTesting();
 
-  mojom::SecureChannelPtr secure_channel_ptr_;
+  mojo::Remote<mojom::SecureChannel> secure_channel_remote_;
 
   scoped_refptr<base::TaskRunner> task_runner_;
 
diff --git a/chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl_unittest.cc b/chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl_unittest.cc
index b74ba3c..56604c7 100644
--- a/chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl_unittest.cc
+++ b/chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl_unittest.cc
@@ -150,7 +150,7 @@
         test_task_runner_);
 
     mojo::PendingRemote<mojom::SecureChannel> channel;
-    service_->BindRequest(channel.InitWithNewPipeAndPassReceiver());
+    service_->BindReceiver(channel.InitWithNewPipeAndPassReceiver());
     client_ = SecureChannelClientImpl::Factory::Get()->BuildInstance(
         std::move(channel), test_task_runner_);
   }
diff --git a/chromeos/services/secure_channel/public/mojom/secure_channel.mojom b/chromeos/services/secure_channel/public/mojom/secure_channel.mojom
index 2547cb2..a7065ee7 100644
--- a/chromeos/services/secure_channel/public/mojom/secure_channel.mojom
+++ b/chromeos/services/secure_channel/public/mojom/secure_channel.mojom
@@ -174,7 +174,7 @@
       chromeos.multidevice.mojom.RemoteDevice local_device,
       string feature,
       ConnectionPriority connection_priority,
-      ConnectionDelegate delegate);
+      pending_remote<ConnectionDelegate> delegate);
 
   // Initiates a connection to |device|, which is presumed to be listening
   // for incoming connections. This function attempts to create a connection
@@ -188,5 +188,5 @@
       chromeos.multidevice.mojom.RemoteDevice local_device,
       string feature,
       ConnectionPriority connection_priority,
-      ConnectionDelegate delegate);
+      pending_remote<ConnectionDelegate> delegate);
 };
diff --git a/chromeos/services/secure_channel/secure_channel_base.cc b/chromeos/services/secure_channel/secure_channel_base.cc
index 7c3b4f0..df77af1 100644
--- a/chromeos/services/secure_channel/secure_channel_base.cc
+++ b/chromeos/services/secure_channel/secure_channel_base.cc
@@ -12,8 +12,9 @@
 
 SecureChannelBase::~SecureChannelBase() = default;
 
-void SecureChannelBase::BindRequest(mojom::SecureChannelRequest request) {
-  bindings_.AddBinding(this, std::move(request));
+void SecureChannelBase::BindReceiver(
+    mojo::PendingReceiver<mojom::SecureChannel> receiver) {
+  receivers_.Add(this, std::move(receiver));
 }
 
 }  // namespace secure_channel
diff --git a/chromeos/services/secure_channel/secure_channel_base.h b/chromeos/services/secure_channel/secure_channel_base.h
index 85e5c88..e9c7d36 100644
--- a/chromeos/services/secure_channel/secure_channel_base.h
+++ b/chromeos/services/secure_channel/secure_channel_base.h
@@ -7,7 +7,8 @@
 
 #include "base/macros.h"
 #include "chromeos/services/secure_channel/public/mojom/secure_channel.mojom.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 
 namespace chromeos {
 
@@ -18,15 +19,15 @@
  public:
   ~SecureChannelBase() override;
 
-  // Binds a request to this implementation. Should be called each time that the
-  // service receives a request.
-  void BindRequest(mojom::SecureChannelRequest request);
+  // Binds a receiver to this implementation. Should be called each time that
+  // the service receives a receiver.
+  void BindReceiver(mojo::PendingReceiver<mojom::SecureChannel> receiver);
 
  protected:
   SecureChannelBase();
 
  private:
-  mojo::BindingSet<mojom::SecureChannel> bindings_;
+  mojo::ReceiverSet<mojom::SecureChannel> receivers_;
 
   DISALLOW_COPY_AND_ASSIGN(SecureChannelBase);
 };
diff --git a/chromeos/services/secure_channel/secure_channel_impl.cc b/chromeos/services/secure_channel/secure_channel_impl.cc
index ce64280..0c47e5f 100644
--- a/chromeos/services/secure_channel/secure_channel_impl.cc
+++ b/chromeos/services/secure_channel/secure_channel_impl.cc
@@ -99,7 +99,7 @@
     const multidevice::RemoteDevice& local_device,
     const std::string& feature,
     ConnectionPriority connection_priority,
-    mojom::ConnectionDelegatePtr delegate) {
+    mojo::PendingRemote<mojom::ConnectionDelegate> delegate) {
   ProcessConnectionRequest(
       ApiFunctionName::kListenForConnection, device_to_connect, local_device,
       ClientConnectionParametersImpl::Factory::Get()->BuildInstance(
@@ -113,7 +113,7 @@
     const multidevice::RemoteDevice& local_device,
     const std::string& feature,
     ConnectionPriority connection_priority,
-    mojom::ConnectionDelegatePtr delegate) {
+    mojo::PendingRemote<mojom::ConnectionDelegate> delegate) {
   ProcessConnectionRequest(
       ApiFunctionName::kInitiateConnection, device_to_connect, local_device,
       ClientConnectionParametersImpl::Factory::Get()->BuildInstance(
diff --git a/chromeos/services/secure_channel/secure_channel_impl.h b/chromeos/services/secure_channel/secure_channel_impl.h
index 61029d3..178e8de 100644
--- a/chromeos/services/secure_channel/secure_channel_impl.h
+++ b/chromeos/services/secure_channel/secure_channel_impl.h
@@ -18,6 +18,7 @@
 #include "chromeos/services/secure_channel/pending_connection_manager.h"
 #include "chromeos/services/secure_channel/public/cpp/shared/connection_priority.h"
 #include "chromeos/services/secure_channel/public/mojom/secure_channel.mojom.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace device {
 class BluetoothAdapter;
@@ -93,13 +94,13 @@
       const multidevice::RemoteDevice& local_device,
       const std::string& feature,
       ConnectionPriority connection_priority,
-      mojom::ConnectionDelegatePtr delegate) override;
+      mojo::PendingRemote<mojom::ConnectionDelegate> delegate) override;
   void InitiateConnectionToDevice(
       const multidevice::RemoteDevice& device_to_connect,
       const multidevice::RemoteDevice& local_device,
       const std::string& feature,
       ConnectionPriority connection_priority,
-      mojom::ConnectionDelegatePtr delegate) override;
+      mojo::PendingRemote<mojom::ConnectionDelegate> delegate) override;
 
   // ActiveConnectionManager::Delegate:
   void OnDisconnected(const ConnectionDetails& connection_details) override;
diff --git a/chromeos/services/secure_channel/secure_channel_initializer.cc b/chromeos/services/secure_channel/secure_channel_initializer.cc
index c53b99a..3fd3422 100644
--- a/chromeos/services/secure_channel/secure_channel_initializer.cc
+++ b/chromeos/services/secure_channel/secure_channel_initializer.cc
@@ -47,7 +47,7 @@
     const multidevice::RemoteDevice& local_device,
     const std::string& feature,
     ConnectionPriority connection_priority,
-    mojom::ConnectionDelegatePtr delegate,
+    mojo::PendingRemote<mojom::ConnectionDelegate> delegate,
     bool is_listen_request)
     : device_to_connect(device_to_connect),
       local_device(local_device),
@@ -83,7 +83,7 @@
     const multidevice::RemoteDevice& local_device,
     const std::string& feature,
     ConnectionPriority connection_priority,
-    mojom::ConnectionDelegatePtr delegate) {
+    mojo::PendingRemote<mojom::ConnectionDelegate> delegate) {
   if (secure_channel_impl_) {
     secure_channel_impl_->ListenForConnectionFromDevice(
         device_to_connect, local_device, feature, connection_priority,
@@ -101,7 +101,7 @@
     const multidevice::RemoteDevice& local_device,
     const std::string& feature,
     ConnectionPriority connection_priority,
-    mojom::ConnectionDelegatePtr delegate) {
+    mojo::PendingRemote<mojom::ConnectionDelegate> delegate) {
   if (secure_channel_impl_) {
     secure_channel_impl_->InitiateConnectionToDevice(
         device_to_connect, local_device, feature, connection_priority,
diff --git a/chromeos/services/secure_channel/secure_channel_initializer.h b/chromeos/services/secure_channel/secure_channel_initializer.h
index 91f6003..d99ced1 100644
--- a/chromeos/services/secure_channel/secure_channel_initializer.h
+++ b/chromeos/services/secure_channel/secure_channel_initializer.h
@@ -15,6 +15,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "chromeos/services/secure_channel/public/mojom/secure_channel.mojom.h"
 #include "chromeos/services/secure_channel/secure_channel_base.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace device {
 class BluetoothAdapter;
@@ -52,19 +53,20 @@
       scoped_refptr<base::TaskRunner> task_runner);
 
   struct ConnectionRequestArgs {
-    ConnectionRequestArgs(const multidevice::RemoteDevice& device_to_connect,
-                          const multidevice::RemoteDevice& local_device,
-                          const std::string& feature,
-                          ConnectionPriority connection_priority,
-                          mojom::ConnectionDelegatePtr delegate,
-                          bool is_listen_request);
+    ConnectionRequestArgs(
+        const multidevice::RemoteDevice& device_to_connect,
+        const multidevice::RemoteDevice& local_device,
+        const std::string& feature,
+        ConnectionPriority connection_priority,
+        mojo::PendingRemote<mojom::ConnectionDelegate> delegate,
+        bool is_listen_request);
     ~ConnectionRequestArgs();
 
     multidevice::RemoteDevice device_to_connect;
     multidevice::RemoteDevice local_device;
     std::string feature;
     ConnectionPriority connection_priority;
-    mojom::ConnectionDelegatePtr delegate;
+    mojo::PendingRemote<mojom::ConnectionDelegate> delegate;
     bool is_listen_request;
   };
 
@@ -74,13 +76,13 @@
       const multidevice::RemoteDevice& local_device,
       const std::string& feature,
       ConnectionPriority connection_priority,
-      mojom::ConnectionDelegatePtr delegate) override;
+      mojo::PendingRemote<mojom::ConnectionDelegate> delegate) override;
   void InitiateConnectionToDevice(
       const multidevice::RemoteDevice& device_to_connect,
       const multidevice::RemoteDevice& local_device,
       const std::string& feature,
       ConnectionPriority connection_priority,
-      mojom::ConnectionDelegatePtr delegate) override;
+      mojo::PendingRemote<mojom::ConnectionDelegate> delegate) override;
 
   void OnBluetoothAdapterReceived(
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter);
diff --git a/chromeos/services/secure_channel/secure_channel_service_unittest.cc b/chromeos/services/secure_channel/secure_channel_service_unittest.cc
index a2cbb64e..82e2935 100644
--- a/chromeos/services/secure_channel/secure_channel_service_unittest.cc
+++ b/chromeos/services/secure_channel/secure_channel_service_unittest.cc
@@ -33,6 +33,8 @@
 #include "chromeos/services/secure_channel/timer_factory_impl.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
@@ -273,7 +275,8 @@
   // ClientConnectionParametersImpl::Factory:
   std::unique_ptr<ClientConnectionParameters> BuildInstance(
       const std::string& feature,
-      mojom::ConnectionDelegatePtr connection_delegate_ptr) override {
+      mojo::PendingRemote<mojom::ConnectionDelegate> connection_delegate_remote)
+      override {
     auto instance = std::make_unique<FakeClientConnectionParameters>(
         feature, base::BindOnce(
                      &FakeClientConnectionParametersFactory::OnInstanceDeleted,
@@ -377,8 +380,8 @@
         fake_client_connection_parameters_factory_.get());
 
     service_ = SecureChannelInitializer::Factory::Get()->BuildInstance();
-    service_->BindRequest(mojo::MakeRequest(&secure_channel_ptr_));
-    secure_channel_ptr_.FlushForTesting();
+    service_->BindReceiver(secure_channel_remote_.BindNewPipeAndPassReceiver());
+    secure_channel_remote_.FlushForTesting();
   }
 
   void TearDown() override {
@@ -802,16 +805,16 @@
     FakeConnectionDelegate fake_connection_delegate;
 
     if (is_listener) {
-      secure_channel_ptr_->ListenForConnectionFromDevice(
+      secure_channel_remote_->ListenForConnectionFromDevice(
           device_to_connect, local_device, feature, connection_priority,
-          fake_connection_delegate.GenerateInterfacePtr());
+          fake_connection_delegate.GenerateRemote());
     } else {
-      secure_channel_ptr_->InitiateConnectionToDevice(
+      secure_channel_remote_->InitiateConnectionToDevice(
           device_to_connect, local_device, feature, connection_priority,
-          fake_connection_delegate.GenerateInterfacePtr());
+          fake_connection_delegate.GenerateRemote());
     }
 
-    secure_channel_ptr_.FlushForTesting();
+    secure_channel_remote_.FlushForTesting();
   }
 
   FakeActiveConnectionManager* fake_active_connection_manager() {
@@ -864,7 +867,7 @@
   bool is_adapter_powered_;
   bool is_adapter_present_;
 
-  mojom::SecureChannelPtr secure_channel_ptr_;
+  mojo::Remote<mojom::SecureChannel> secure_channel_remote_;
 
   DISALLOW_COPY_AND_ASSIGN(SecureChannelServiceTest);
 };
diff --git a/components/about_ui/resources/about_credits.tmpl b/components/about_ui/resources/about_credits.tmpl
index 9cf3f9a..a03ba3ed7 100644
--- a/components/about_ui/resources/about_credits.tmpl
+++ b/components/about_ui/resources/about_credits.tmpl
@@ -3,20 +3,55 @@
 <head>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width">
+<meta name="color-scheme" content="light dark">
 <title>Credits</title>
 <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
 <style>
+html {
+  --google-blue-50: rgb(232, 240, 254);
+  --google-blue-300: rgb(138, 180, 248);
+  --google-blue-600: rgb(26, 115, 232);
+  --google-blue-900: rgb(23, 78, 166);
+  --google-grey-200: rgb(232, 234, 237);
+  --google-grey-800: rgb(60, 64, 67);
+  --google-grey-900: rgb(32, 33, 36);
+
+  --interactive-color: var(--google-blue-600);
+  --primary-color: var(--google-grey-900);
+
+  --product-background: var(--google-blue-50);
+  --product-text-color: var(--google-blue-900);
+
+  background: white;
+}
+
+@media (prefers-color-scheme: dark) {
+  html {
+    --interactive-color: var(--google-blue-300);
+    --primary-color: var(--google-grey-200);
+
+    --product-background: var(--google-grey-800);
+    --product-text-color: var(--google-grey-200);
+
+    background: var(--google-grey-900);
+  }
+}
+
 body {
-  background-color: white;
+  color: var(--primary-color);
   font-size: 84%;
   max-width: 1020px;
 }
+a {
+  color: var(--interactive-color);
+}
 .page-title {
   font-size: 164%;
   font-weight: bold;
 }
 .product {
-  background-color: #c3d9ff;
+  background-color: var(--product-background);
+  color: var(--product-text-color);
   border-radius: 5px;
   margin-top: 16px;
   overflow: auto;
@@ -29,7 +64,7 @@
   margin: 3px;
 }
 .product .homepage {
-  color: blue;
+  color: var(--interactive-color);
   float: right;
   margin: 3px;
   text-align: right;
@@ -38,14 +73,13 @@
   content: " - ";
 }
 .product .show {
-  color: blue;
+  color: var(--interactive-color);
   float: right;
   margin: 3px;
   text-align: right;
   text-decoration: underline;
 }
 .licence {
-  background-color: #e8eef7;
   border-radius: 3px;
   clear: both;
   display: none;
diff --git a/components/arc/video_accelerator/BUILD.gn b/components/arc/video_accelerator/BUILD.gn
index 80ce9c3..2dbf2b4 100644
--- a/components/arc/video_accelerator/BUILD.gn
+++ b/components/arc/video_accelerator/BUILD.gn
@@ -19,6 +19,7 @@
   deps = [
     ":common",
     "//components/arc/mojom:media",
+    "//gpu/ipc/common:common",
     "//media",
     "//ui/ozone",
   ]
diff --git a/components/arc/video_accelerator/DEPS b/components/arc/video_accelerator/DEPS
index affce5e..86036be 100644
--- a/components/arc/video_accelerator/DEPS
+++ b/components/arc/video_accelerator/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+components/arc/mojom",
   "+gpu/config/gpu_preferences.h",
+  "+gpu/ipc/common/gpu_memory_buffer_support.h",
   "+media/base",
   "+media/gpu",
   "+media/video",
diff --git a/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc b/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
index 214cf3b..1af2343 100644
--- a/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
+++ b/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
@@ -14,6 +14,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/system/sys_info.h"
 #include "components/arc/video_accelerator/arc_video_accelerator_util.h"
+#include "media/base/format_utils.h"
 #include "media/base/video_types.h"
 #include "media/gpu/gpu_video_encode_accelerator_factory.h"
 #include "media/gpu/macros.h"
@@ -188,19 +189,24 @@
     return;
   }
 
-  auto layout = CreateVideoFrameLayout(format, coded_size_, *gmb_handle);
-  if (!layout) {
-    DLOG(ERROR) << "Failed to create VideoFrameLayout.";
+  base::Optional<gfx::BufferFormat> buffer_format =
+      VideoPixelFormatToGfxBufferFormat(format);
+  if (!format) {
+    DLOG(ERROR) << "Unexpected format: " << format;
     client_->NotifyError(Error::kInvalidArgumentError);
     return;
   }
+  std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
+      support_.CreateGpuMemoryBufferImplFromHandle(
+          std::move(gmb_handle).value(), coded_size_, *buffer_format,
+          gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE,
+          base::NullCallback());
 
-  std::vector<base::ScopedFD> scoped_fds;
-  for (auto& plane : gmb_handle->native_pixmap_handle.planes) {
-    scoped_fds.push_back(std::move(plane.fd));
-  }
-  auto frame = media::VideoFrame::WrapExternalDmabufs(
-      *layout, gfx::Rect(visible_size_), visible_size_, std::move(scoped_fds),
+  gpu::MailboxHolder dummy_mailbox[media::VideoFrame::kMaxPlanes];
+  auto frame = media::VideoFrame::WrapExternalGpuMemoryBuffer(
+      gfx::Rect(visible_size_), visible_size_, std::move(gpu_memory_buffer),
+      dummy_mailbox /* mailbox_holders */,
+      base::NullCallback() /* mailbox_holder_release_cb_ */,
       base::TimeDelta::FromMicroseconds(timestamp));
   if (!frame) {
     DLOG(ERROR) << "Failed to create VideoFrame";
diff --git a/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h b/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h
index 523e04f..aed1f30 100644
--- a/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h
+++ b/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h
@@ -14,6 +14,7 @@
 #include "components/arc/mojom/video_encode_accelerator.mojom.h"
 #include "components/arc/video_accelerator/video_frame_plane.h"
 #include "gpu/config/gpu_preferences.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/video/video_encode_accelerator.h"
 
 namespace arc {
@@ -84,6 +85,7 @@
   media::VideoEncodeAccelerator::Config::StorageType input_storage_type_;
   int32_t bitstream_buffer_serial_;
   std::unordered_map<uint32_t, UseBitstreamBufferCallback> use_bitstream_cbs_;
+  gpu::GpuMemoryBufferSupport support_;
 
   DISALLOW_COPY_AND_ASSIGN(GpuArcVideoEncodeAccelerator);
 };
diff --git a/components/autofill/content/common/mojom/autofill_agent.mojom b/components/autofill/content/common/mojom/autofill_agent.mojom
index e7b25eab..ce88fc20 100644
--- a/components/autofill/content/common/mojom/autofill_agent.mojom
+++ b/components/autofill/content/common/mojom/autofill_agent.mojom
@@ -92,10 +92,6 @@
   // logging the decisions made about saving the password.
   SetLoggingState(bool active);
 
-  // Sent when Autofill manager gets the query response from the Autofill server
-  // which contains information about username and password for some forms.
-  // |predictions| maps forms to their username fields.
-  AutofillUsernameAndPasswordDataReceived(FormsPredictionsMap predictions);
 };
 
 // There is one instance of this interface per render frame in the render
diff --git a/components/autofill/content/renderer/BUILD.gn b/components/autofill/content/renderer/BUILD.gn
index 95d49af..f36cc9d 100644
--- a/components/autofill/content/renderer/BUILD.gn
+++ b/components/autofill/content/renderer/BUILD.gn
@@ -32,8 +32,6 @@
     "password_generation_agent.h",
     "prefilled_values_detector.cc",
     "prefilled_values_detector.h",
-    "provisionally_saved_password_form.cc",
-    "provisionally_saved_password_form.h",
     "renderer_save_password_progress_logger.cc",
     "renderer_save_password_progress_logger.h",
   ]
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index 2d96baf..877419e 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -1980,23 +1980,6 @@
                            &FillFormField);
 }
 
-void FillFormIncludingNonFocusableElements(const FormData& form_data,
-                                           const WebFormElement& form_element) {
-  if (form_element.IsNull()) {
-    NOTREACHED();
-    return;
-  }
-
-  FieldFilterMask filter_mask = static_cast<FieldFilterMask>(
-      FILTER_DISABLED_ELEMENTS | FILTER_READONLY_ELEMENTS);
-  ForEachMatchingFormField(form_element,
-                           WebInputElement(),
-                           form_data,
-                           filter_mask,
-                           true, /* force override */
-                           &FillFormField);
-}
-
 void PreviewForm(const FormData& form, const WebFormControlElement& element) {
   WebFormElement form_element = element.Form();
   if (form_element.IsNull()) {
diff --git a/components/autofill/content/renderer/form_autofill_util.h b/components/autofill/content/renderer/form_autofill_util.h
index bb82da8..8bbe645 100644
--- a/components/autofill/content/renderer/form_autofill_util.h
+++ b/components/autofill/content/renderer/form_autofill_util.h
@@ -219,12 +219,6 @@
 void FillForm(const FormData& form,
               const blink::WebFormControlElement& element);
 
-// Fills focusable and non-focusable form control elements within |form_element|
-// with field data from |form_data|.
-void FillFormIncludingNonFocusableElements(
-    const FormData& form_data,
-    const blink::WebFormElement& form_element);
-
 // Previews the form represented by |form|.  |element| is the input element that
 // initiated the preview process.
 void PreviewForm(const FormData& form,
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index e5006fd0..54e9884 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -888,9 +888,8 @@
 
 void PasswordAutofillAgent::FireSubmissionIfFormDisappear(
     SubmissionIndicatorEvent event) {
-  if (!provisionally_saved_form_.IsPasswordValid())
+  if (!browser_has_form_to_process_)
     return;
-
   DCHECK(FrameCanAccessPasswordManager());
 
   // Prompt to save only if the form is now gone, either invisible or
@@ -914,10 +913,8 @@
         return;
     }
   }
-
-  provisionally_saved_form_.SetSubmissionIndicatorEvent(event);
   GetPasswordManagerDriver()->SameDocumentNavigation(event);
-  provisionally_saved_form_.Reset();
+  browser_has_form_to_process_ = false;
 }
 
 void PasswordAutofillAgent::UserGestureObserved() {
@@ -1101,11 +1098,8 @@
   // If a sub frame has been destroyed while the user was entering information
   // into a password form, try to save the data. See https://crbug.com/450806
   // for examples of sites that perform login using this technique.
-  if (render_frame()->GetWebFrame()->Parent() &&
-      provisionally_saved_form_.IsPasswordValid()) {
+  if (render_frame()->GetWebFrame()->Parent() && browser_has_form_to_process_) {
     DCHECK(FrameCanAccessPasswordManager());
-    provisionally_saved_form_.SetSubmissionIndicatorEvent(
-        SubmissionIndicatorEvent::FRAME_DETACHED);
     GetPasswordManagerDriver()->SameDocumentNavigation(
         SubmissionIndicatorEvent::FRAME_DETACHED);
   }
@@ -1124,18 +1118,6 @@
   std::unique_ptr<PasswordForm> submitted_form =
       GetPasswordFormFromWebForm(form);
 
-  // As a site may clear field values, use a provisionally saved form as
-  // the submitted form.
-  if (provisionally_saved_form_.IsSet() &&
-      (!submitted_form ||
-       submitted_form->action ==
-           provisionally_saved_form_.password_form().action)) {
-    submitted_form.reset(
-        new PasswordForm(provisionally_saved_form_.password_form()));
-    if (logger)
-      logger->LogMessage(Logger::STRING_SUBMITTED_PASSWORD_REPLACED);
-  }
-
   // If there is a provisionally saved password, copy over the previous
   // password value so we get the user's typed password, not the value that
   // may have been transformed for submit.
@@ -1161,8 +1143,7 @@
       if (logger)
         logger->LogMessage(Logger::STRING_SECURITY_ORIGIN_FAILURE);
     }
-
-    provisionally_saved_form_.Reset();
+    browser_has_form_to_process_ = false;
   } else if (logger) {
     logger->LogMessage(Logger::STRING_FORM_IS_NOT_PASSWORD);
   }
@@ -1339,11 +1320,6 @@
   logging_state_active_ = active;
 }
 
-void PasswordAutofillAgent::AutofillUsernameAndPasswordDataReceived(
-    const FormsPredictionsMap& predictions) {
-  form_predictions_.insert(predictions.begin(), predictions.end());
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // PasswordAutofillAgent, private:
 
@@ -1392,7 +1368,7 @@
   web_input_to_password_info_.clear();
   password_to_username_.clear();
   last_supplied_password_info_iter_ = web_input_to_password_info_.end();
-  provisionally_saved_form_.Reset();
+  browser_has_form_to_process_ = false;
   field_data_manager_.ClearData();
   username_autofill_state_ = WebAutofillState::kNotFilled;
   password_autofill_state_ = WebAutofillState::kNotFilled;
@@ -1447,14 +1423,13 @@
   if (!FrameCanAccessPasswordManager())
     return;
 
-  provisionally_saved_form_.Set(std::move(password_form), form, element);
 
   if (has_password) {
-    GetPasswordManagerDriver()->ShowManualFallbackForSaving(
-        provisionally_saved_form_.password_form());
+    GetPasswordManagerDriver()->ShowManualFallbackForSaving(*password_form);
   } else {
     GetPasswordManagerDriver()->HideManualFallbackForSaving();
   }
+  browser_has_form_to_process_ = true;
 }
 
 bool PasswordAutofillAgent::FillUserNameAndPassword(
@@ -1615,19 +1590,6 @@
   }
 
   DCHECK_EQ(ElementChangeSource::WILL_SEND_SUBMIT_EVENT, source);
-  // Forms submitted via XHR are not seen by WillSubmitForm if the default
-  // onsubmit handler is overridden. Such submission first gets detected in
-  // DidStartProvisionalLoad, which no longer knows about the particular form,
-  // and uses the candidate stored in |provisionally_saved_form_|.
-  //
-  // User-typed password will get stored to |provisionally_saved_form_| in
-  // TextDidChangeInTextField. Autofilled or JavaScript-copied passwords need to
-  // be saved here.
-  //
-  // Only non-empty passwords are saved here. Empty passwords were likely
-  // cleared by some scripts (http://crbug.com/28910, http://crbug.com/391693).
-  // Had the user cleared the password, |provisionally_saved_form_| would
-  // already have been updated in TextDidChangeInTextField.
   ProvisionallySavePassword(form, input_element,
                             RESTRICTION_NON_EMPTY_PASSWORD);
 }
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index a957b5c..6bb319a 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -20,7 +20,6 @@
 #include "components/autofill/content/renderer/field_data_manager.h"
 #include "components/autofill/content/renderer/form_tracker.h"
 #include "components/autofill/content/renderer/html_based_username_detector.h"
-#include "components/autofill/content/renderer/provisionally_saved_password_form.h"
 #include "components/autofill/core/common/form_data_predictions.h"
 #include "components/autofill/core/common/mojom/autofill_types.mojom.h"
 #include "components/autofill/core/common/password_form.h"
@@ -132,8 +131,6 @@
   void FillIntoFocusedField(bool is_password,
                             const base::string16& credential) override;
   void SetLoggingState(bool active) override;
-  void AutofillUsernameAndPasswordDataReceived(
-      const FormsPredictionsMap& predictions) override;
 
   // FormTracker::Observer
   void OnProvisionallySaveForm(const blink::WebFormElement& form,
@@ -458,10 +455,6 @@
   // The chronologically last insertion into |web_input_to_password_info_|.
   WebInputToPasswordInfoMap::iterator last_supplied_password_info_iter_;
 
-  // Set if the user might be submitting a password form on the current page,
-  // but the submit may still fail (i.e. doesn't pass JavaScript validation).
-  ProvisionallySavedPasswordForm provisionally_saved_form_;
-
   // Map WebFormControlElement to the pair of:
   // 1) The most recent text that user typed or PasswordManager autofilled in
   // input elements. Used for storing username/password before JavaScript
@@ -489,6 +482,12 @@
   // True indicates that a request for credentials has been sent to the store.
   bool sent_request_to_store_;
 
+  // True indicates that a form data has been sent to the browser process. Gets
+  // cleared when the form is submitted to indicate that the browser has already
+  // processed the form.
+  // TODO(crbug.com/949519): double check if we need this variable.
+  bool browser_has_form_to_process_ = false;
+
   // True indicates that a safe browsing reputation check has been triggered.
   bool checked_safe_browsing_reputation_;
 
diff --git a/components/autofill/content/renderer/provisionally_saved_password_form.cc b/components/autofill/content/renderer/provisionally_saved_password_form.cc
deleted file mode 100644
index 376eddf..0000000
--- a/components/autofill/content/renderer/provisionally_saved_password_form.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/content/renderer/provisionally_saved_password_form.h"
-
-#include <utility>
-
-namespace autofill {
-
-ProvisionallySavedPasswordForm::ProvisionallySavedPasswordForm() = default;
-
-ProvisionallySavedPasswordForm::~ProvisionallySavedPasswordForm() = default;
-
-void ProvisionallySavedPasswordForm::Set(
-    std::unique_ptr<PasswordForm> password_form,
-    const blink::WebFormElement& form_element,
-    const blink::WebInputElement& input_element) {
-  password_form_ = std::move(password_form);
-  form_element_ = form_element;
-  input_element_ = input_element;
-}
-
-void ProvisionallySavedPasswordForm::Reset() {
-  password_form_.reset();
-  form_element_.Reset();
-  input_element_.Reset();
-}
-
-bool ProvisionallySavedPasswordForm::IsSet() const {
-  return static_cast<bool>(password_form_);
-}
-
-bool ProvisionallySavedPasswordForm::IsPasswordValid() const {
-  return IsSet() && !(password_form_->password_value.empty() &&
-                      password_form_->new_password_value.empty());
-}
-
-void ProvisionallySavedPasswordForm::SetSubmissionIndicatorEvent(
-    mojom::SubmissionIndicatorEvent event) {
-  if (password_form_) {
-    password_form_->submission_event = event;
-    password_form_->form_data.submission_event = event;
-  }
-}
-
-}  // namespace autofill
diff --git a/components/autofill/content/renderer/provisionally_saved_password_form.h b/components/autofill/content/renderer/provisionally_saved_password_form.h
deleted file mode 100644
index 89e9b11f..0000000
--- a/components/autofill/content/renderer/provisionally_saved_password_form.h
+++ /dev/null
@@ -1,62 +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 COMPONENTS_AUTOFILL_CONTENT_RENDERER_PROVISIONALLY_SAVED_PASSWORD_FORM_H_
-#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_PROVISIONALLY_SAVED_PASSWORD_FORM_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "components/autofill/core/common/mojom/autofill_types.mojom.h"
-#include "components/autofill/core/common/password_form.h"
-#include "third_party/blink/public/web/web_input_element.h"
-
-namespace autofill {
-
-struct PasswordForm;
-
-// Represents a possibly submitted password form.
-class ProvisionallySavedPasswordForm {
- public:
-  ProvisionallySavedPasswordForm();
-  ~ProvisionallySavedPasswordForm();
-
-  // Sets the PasswordForm and web elements that were used in the PasswordForm
-  // update.
-  void Set(std::unique_ptr<PasswordForm> password_form,
-           const blink::WebFormElement& form_element,
-           const blink::WebInputElement& input_element);
-  void Reset();
-
-  // Returns true if the instance has |password_form_| set, but the actual
-  // password data may be invalid (e.g. empty username or password).
-  bool IsSet() const;
-  // Returns true if |password_form_| has enough information that it is likely
-  // filled out.
-  bool IsPasswordValid() const;
-
-  const PasswordForm& password_form() const {
-    DCHECK(IsSet());
-    return *password_form_;
-  }
-  blink::WebFormElement& form_element() { return form_element_; }
-  blink::WebInputElement& input_element() { return input_element_; }
-
-  void SetSubmissionIndicatorEvent(mojom::SubmissionIndicatorEvent event);
-
- private:
-  std::unique_ptr<PasswordForm> password_form_;
-  // Last used WebFormElement for the PasswordForm submission. Can be null if
-  // the form is unowned.
-  blink::WebFormElement form_element_;
-  // Last used WebInputElement which led to the PasswordForm update. Can be null
-  // if the user has performed a form submission (via a button, for example).
-  blink::WebInputElement input_element_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProvisionallySavedPasswordForm);
-};
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CONTENT_RENDERER_PROVISIONALLY_SAVED_PASSWORD_FORM_H_
diff --git a/components/autofill/core/browser/autofill_metrics.h b/components/autofill/core/browser/autofill_metrics.h
index 608a7b3..912f94b 100644
--- a/components/autofill/core/browser/autofill_metrics.h
+++ b/components/autofill/core/browser/autofill_metrics.h
@@ -1263,13 +1263,6 @@
   // autofilled to support synthetic fields.
   static void LogHiddenOrPresentationalSelectFieldsFilled();
 
-  // Logs the the |ukm_entry_name| with the specified |url| and the specified
-  // |metrics|. Returns whether the ukm was sucessfully logged.
-  static bool LogUkm(ukm::UkmRecorder* ukm_recorder,
-                     const GURL& url,
-                     const std::string& ukm_entry_name,
-                     const std::vector<std::pair<const char*, int>>& metrics);
-
   // Converts form type to bit vector to store in UKM.
   static int64_t FormTypesToBitVector(const std::set<FormType>& form_types);
 
diff --git a/components/autofill/core/browser/autofill_type.cc b/components/autofill/core/browser/autofill_type.cc
index d141a0db..7b673f6 100644
--- a/components/autofill/core/browser/autofill_type.cc
+++ b/components/autofill/core/browser/autofill_type.cc
@@ -439,78 +439,6 @@
   return UNKNOWN_TYPE;
 }
 
-// static
-ServerFieldType AutofillType::GetEquivalentBillingFieldType(
-    ServerFieldType field_type) {
-  switch (field_type) {
-    case ADDRESS_HOME_LINE1:
-      return ADDRESS_BILLING_LINE1;
-
-    case ADDRESS_HOME_LINE2:
-      return ADDRESS_BILLING_LINE2;
-
-    case ADDRESS_HOME_APT_NUM:
-      return ADDRESS_BILLING_APT_NUM;
-
-    case ADDRESS_HOME_CITY:
-      return ADDRESS_BILLING_CITY;
-
-    case ADDRESS_HOME_STATE:
-      return ADDRESS_BILLING_STATE;
-
-    case ADDRESS_HOME_ZIP:
-      return ADDRESS_BILLING_ZIP;
-
-    case ADDRESS_HOME_COUNTRY:
-      return ADDRESS_BILLING_COUNTRY;
-
-    case ADDRESS_HOME_STREET_ADDRESS:
-      return ADDRESS_BILLING_STREET_ADDRESS;
-
-    case ADDRESS_HOME_SORTING_CODE:
-      return ADDRESS_BILLING_SORTING_CODE;
-
-    case ADDRESS_HOME_DEPENDENT_LOCALITY:
-      return ADDRESS_BILLING_DEPENDENT_LOCALITY;
-
-    case PHONE_HOME_WHOLE_NUMBER:
-      return PHONE_BILLING_WHOLE_NUMBER;
-
-    case PHONE_HOME_NUMBER:
-      return PHONE_BILLING_NUMBER;
-
-    case PHONE_HOME_CITY_CODE:
-      return PHONE_BILLING_CITY_CODE;
-
-    case PHONE_HOME_COUNTRY_CODE:
-      return PHONE_BILLING_COUNTRY_CODE;
-
-    case PHONE_HOME_CITY_AND_NUMBER:
-      return PHONE_BILLING_CITY_AND_NUMBER;
-
-    case NAME_FIRST:
-      return NAME_BILLING_FIRST;
-
-    case NAME_MIDDLE:
-      return NAME_BILLING_MIDDLE;
-
-    case NAME_LAST:
-      return NAME_BILLING_LAST;
-
-    case NAME_MIDDLE_INITIAL:
-      return NAME_BILLING_MIDDLE_INITIAL;
-
-    case NAME_FULL:
-      return NAME_BILLING_FULL;
-
-    case NAME_SUFFIX:
-      return NAME_BILLING_SUFFIX;
-
-    default:
-      return field_type;
-  }
-}
-
 std::string AutofillType::ToString() const {
   if (IsUnknown())
     return "UNKNOWN_TYPE";
diff --git a/components/autofill/core/browser/autofill_type.h b/components/autofill/core/browser/autofill_type.h
index 893567a..09ffd1b 100644
--- a/components/autofill/core/browser/autofill_type.h
+++ b/components/autofill/core/browser/autofill_type.h
@@ -48,11 +48,6 @@
   // Serializes |this| type to a string.
   std::string ToString() const;
 
-  // Maps |field_type| to the corresponding billing field type if the field type
-  // is an address, name, or phone number type.
-  static ServerFieldType GetEquivalentBillingFieldType(
-      ServerFieldType field_type);
-
   // Translates the ServerFieldType values into the corresponding strings.
   static std::string ServerFieldTypeToString(ServerFieldType type);
 
diff --git a/components/autofill/core/browser/data_model/autofill_profile.cc b/components/autofill/core/browser/data_model/autofill_profile.cc
index 125a506b..60aeae6 100644
--- a/components/autofill/core/browser/data_model/autofill_profile.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile.cc
@@ -482,14 +482,6 @@
   return !operator==(profile);
 }
 
-bool AutofillProfile::IsSubsetOf(const AutofillProfile& profile,
-                                 const std::string& app_locale) const {
-  ServerFieldTypeSet types;
-  GetSupportedTypes(&types);
-  return IsSubsetOfForFieldSet(AutofillProfileComparator(app_locale), profile,
-                               app_locale, types);
-}
-
 bool AutofillProfile::IsSubsetOfForFieldSet(
     const AutofillProfileComparator& comparator,
     const AutofillProfile& profile,
diff --git a/components/autofill/core/browser/data_model/autofill_profile.h b/components/autofill/core/browser/data_model/autofill_profile.h
index 768dc510..a984548 100644
--- a/components/autofill/core/browser/data_model/autofill_profile.h
+++ b/components/autofill/core/browser/data_model/autofill_profile.h
@@ -124,10 +124,6 @@
   bool operator==(const AutofillProfile& profile) const;
   virtual bool operator!=(const AutofillProfile& profile) const;
 
-  // Returns true if this AutofillProfile's data is a subset of |profile|'s.
-  bool IsSubsetOf(const AutofillProfile& profile,
-                  const std::string& app_locale) const;
-
   // Like IsSubsetOf, but considers only the given |types|.
   bool IsSubsetOfForFieldSet(const AutofillProfileComparator& comparator,
                              const AutofillProfile& profile,
diff --git a/components/autofill/core/browser/data_model/autofill_profile_unittest.cc b/components/autofill/core/browser/data_model/autofill_profile_unittest.cc
index de503b8f..2315b23 100644
--- a/components/autofill/core/browser/data_model/autofill_profile_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile_unittest.cc
@@ -673,31 +673,6 @@
   EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., Apt. 42"), labels[0]);
 }
 
-TEST(AutofillProfileTest, IsSubsetOfForProfiles) {
-  AutofillProfile profile1 =
-      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
-  test::SetProfileInfo(&profile1, "Genevieve", "", "Fox",
-                       "genevieve@hotmail.com", "", "274 Main St", "",
-                       "Northhampton", "MA", "01060", "US", "");
-
-  AutofillProfile profile2 =
-      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
-  test::SetProfileInfo(&profile2, "Genevieve", "", "Fox",
-                       "genevieve@hotmail.com", "", "", "", "", "", "", "US",
-                       "");
-
-  AutofillProfile profile3 =
-      AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
-  test::SetProfileInfo(&profile3, "Genevieve", "", "Fuller",
-                       "genevieve@hotmail.com", "", "", "", "", "", "", "US",
-                       "");
-
-  EXPECT_FALSE(profile1.IsSubsetOf(profile2, "en-US"));
-  EXPECT_TRUE(profile2.IsSubsetOf(profile1, "en-US"));
-  EXPECT_FALSE(profile2.IsSubsetOf(profile3, "en-US"));
-  EXPECT_FALSE(profile3.IsSubsetOf(profile2, "en-US"));
-}
-
 TEST(AutofillProfileTest, IsSubsetOfForFieldSet_DifferentMiddleNames) {
   AutofillProfile profile1 =
       AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
@@ -1460,6 +1435,7 @@
   }
 }
 
+
 TEST(AutofillProfileTest, ValidityStatesClients) {
   AutofillProfile profile;
 
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index 915cf3f..4ec71b57 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -237,10 +237,6 @@
     return has_author_specified_types_;
   }
 
-  bool has_author_specified_sections() const {
-    return has_author_specified_sections_;
-  }
-
   bool has_author_specified_upi_vpa_hint() const {
     return has_author_specified_upi_vpa_hint_;
   }
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 3c92b9a..41685be 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -1523,30 +1523,6 @@
   return guid;
 }
 
-bool PersonalDataManager::IsCountryOfInterest(
-    const std::string& country_code) const {
-  DCHECK_EQ(2U, country_code.size());
-
-  const std::vector<AutofillProfile*>& profiles = GetProfiles();
-  std::list<std::string> country_codes;
-  for (size_t i = 0; i < profiles.size(); ++i) {
-    country_codes.push_back(base::ToLowerASCII(
-        base::UTF16ToASCII(profiles[i]->GetRawInfo(ADDRESS_HOME_COUNTRY))));
-  }
-
-  std::string timezone_country = CountryCodeForCurrentTimezone();
-  if (!timezone_country.empty())
-    country_codes.push_back(base::ToLowerASCII(timezone_country));
-
-  // Only take the locale into consideration if all else fails.
-  if (country_codes.empty()) {
-    country_codes.push_back(base::ToLowerASCII(
-        AutofillCountry::CountryCodeForLocale(app_locale())));
-  }
-
-  return base::Contains(country_codes, base::ToLowerASCII(country_code));
-}
-
 const std::string& PersonalDataManager::GetDefaultCountryCodeForNewAddress()
     const {
   if (default_country_code_.empty())
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h
index de22eccd..60af2d7 100644
--- a/components/autofill/core/browser/personal_data_manager.h
+++ b/components/autofill/core/browser/personal_data_manager.h
@@ -268,12 +268,6 @@
   // Returns the profiles to suggest to the user, ordered by frecency.
   std::vector<AutofillProfile*> GetProfilesToSuggest() const;
 
-  // Remove profiles that whose |type| field is flagged as invalid, if Chrome
-  // is configured to not make suggestions based on invalid data.
-  static void MaybeRemoveInvalidSuggestions(
-      const AutofillType& type,
-      std::vector<AutofillProfile*>* profiles);
-
   // Returns Suggestions corresponding to the focused field's |type| and
   // |field_contents|, i.e. what the user has typed. |field_is_autofilled| is
   // true if the field has already been autofilled, and |field_types| stores the
@@ -339,12 +333,6 @@
       const std::string& app_locale,
       std::vector<AutofillProfile>* merged_profiles);
 
-  // Returns true if |country_code| is a country that the user is likely to
-  // be associated with the user. More concretely, it checks if there are any
-  // addresses with this country or if the user's system timezone is in the
-  // given country.
-  virtual bool IsCountryOfInterest(const std::string& country_code) const;
-
   // Returns our best guess for the country a user is likely to use when
   // inputting a new address. The value is calculated once and cached, so it
   // will only update when Chrome is restarted.
@@ -710,13 +698,6 @@
   void RemoveAutofillProfileByGUIDAndBlankCreditCardReference(
       const std::string& guid);
 
-  // Returns true if an address can be deleted in a major version upgrade.
-  // An address is deletable if it is unverified, and not used by a valid
-  // credit card as billing address, and not used for a long time(13 months).
-  bool IsAddressDeletable(
-      AutofillProfile* profile,
-      const std::unordered_set<std::string>& used_billing_address_guids);
-
   // Applies various fixes and cleanups on autofill addresses.
   void ApplyAddressFixesAndCleanups();
 
diff --git a/components/autofill/core/browser/webdata/autofill_table.cc b/components/autofill/core/browser/webdata/autofill_table.cc
index 5a8fec2..e3b3e95 100644
--- a/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/components/autofill/core/browser/webdata/autofill_table.cc
@@ -1859,36 +1859,6 @@
   return true;
 }
 
-bool AutofillTable::GetAutofillProfilesInTrash(
-    std::vector<std::string>* guids) {
-  guids->clear();
-
-  sql::Statement s(
-      db_->GetUniqueStatement("SELECT guid FROM autofill_profiles_trash"));
-
-  while (s.Step()) {
-    std::string guid = s.ColumnString(0);
-    guids->push_back(guid);
-  }
-
-  return s.Succeeded();
-}
-
-bool AutofillTable::EmptyAutofillProfilesTrash() {
-  sql::Statement s(
-      db_->GetUniqueStatement("DELETE FROM autofill_profiles_trash"));
-
-  return s.Run();
-}
-
-bool AutofillTable::AddAutofillGUIDToTrash(const std::string& guid) {
-  sql::Statement s(db_->GetUniqueStatement(
-      "INSERT INTO autofill_profiles_trash (guid) VALUES (?)"));
-  s.BindString(0, guid);
-
-  return s.Run();
-}
-
 bool AutofillTable::ClearAutofillProfiles() {
   sql::Statement s1(db_->GetUniqueStatement("DELETE FROM autofill_profiles"));
 
diff --git a/components/autofill/core/browser/webdata/autofill_table.h b/components/autofill/core/browser/webdata/autofill_table.h
index c2a606cc..ba29c015 100644
--- a/components/autofill/core/browser/webdata/autofill_table.h
+++ b/components/autofill/core/browser/webdata/autofill_table.h
@@ -476,17 +476,6 @@
       const base::Time& delete_end,
       std::vector<std::unique_ptr<AutofillProfile>>* profiles);
 
-  // Retrieves all profiles in the database that have been deleted since last
-  // "empty" of the trash.
-  bool GetAutofillProfilesInTrash(std::vector<std::string>* guids);
-
-  // Empties the Autofill profiles "trash can".
-  bool EmptyAutofillProfilesTrash();
-
-  // Retrieves all profiles in the database that have been deleted since last
-  // "empty" of the trash.
-  bool AddAutofillGUIDToTrash(const std::string& guid);
-
   // Clear all profiles.
   bool ClearAutofillProfiles();
 
@@ -540,7 +529,6 @@
   bool MigrateToVersion78AddModelTypeColumns();
   bool MigrateToVersion80AddIsClientValidityStatesUpdatedColumn();
   bool MigrateToVersion81CleanUpWrongModelTypeData();
-  bool MigrateToVersion82AddCloudTokenData();
   // Max data length saved in the table, AKA the maximum length allowed for
   // form data.
   // Copied to components/autofill/ios/browser/resources/autofill_controller.js.
diff --git a/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
index 0209924..b312d259 100644
--- a/components/autofill/core/browser/webdata/autofill_table_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -972,87 +972,6 @@
   EXPECT_FALSE(db_profile);
 }
 
-TEST_F(AutofillTableTest, AutofillProfileTrash) {
-  std::vector<std::string> guids;
-  table_->GetAutofillProfilesInTrash(&guids);
-  EXPECT_TRUE(guids.empty());
-
-  ASSERT_TRUE(
-      table_->AddAutofillGUIDToTrash("00000000-0000-0000-0000-000000000000"));
-  ASSERT_TRUE(
-      table_->AddAutofillGUIDToTrash("00000000-0000-0000-0000-000000000001"));
-  ASSERT_TRUE(table_->GetAutofillProfilesInTrash(&guids));
-  EXPECT_EQ(2UL, guids.size());
-  EXPECT_EQ("00000000-0000-0000-0000-000000000000", guids[0]);
-  EXPECT_EQ("00000000-0000-0000-0000-000000000001", guids[1]);
-
-  ASSERT_TRUE(table_->EmptyAutofillProfilesTrash());
-  ASSERT_TRUE(table_->GetAutofillProfilesInTrash(&guids));
-  EXPECT_TRUE(guids.empty());
-}
-
-TEST_F(AutofillTableTest, AutofillProfileTrashInteraction) {
-  std::vector<std::string> guids;
-  table_->GetAutofillProfilesInTrash(&guids);
-  EXPECT_TRUE(guids.empty());
-
-  AutofillProfile profile;
-  profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
-  profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Q."));
-  profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith"));
-  profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@smith.xyz"));
-  profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1 Main St"));
-  profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Los Angeles"));
-  profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA"));
-  profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90025"));
-  profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
-
-  // Mark this profile as in the trash.  This stops |AddAutofillProfile| from
-  // adding it.
-  EXPECT_TRUE(table_->AddAutofillGUIDToTrash(profile.guid()));
-  EXPECT_TRUE(table_->AddAutofillProfile(profile));
-  std::unique_ptr<AutofillProfile> added_profile =
-      table_->GetAutofillProfile(profile.guid());
-  EXPECT_FALSE(added_profile);
-
-  // Add the profile for real this time.
-  EXPECT_TRUE(table_->EmptyAutofillProfilesTrash());
-  EXPECT_TRUE(table_->GetAutofillProfilesInTrash(&guids));
-  EXPECT_TRUE(guids.empty());
-  EXPECT_TRUE(table_->AddAutofillProfile(profile));
-  added_profile = table_->GetAutofillProfile(profile.guid());
-  EXPECT_TRUE(added_profile);
-
-  // Mark this profile as in the trash.  This stops |UpdateAutofillProfileMulti|
-  // from updating it.  In normal operation a profile should not be both in the
-  // trash and in the profiles table simultaneously.
-  EXPECT_TRUE(table_->AddAutofillGUIDToTrash(profile.guid()));
-  profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
-  EXPECT_TRUE(table_->UpdateAutofillProfile(profile));
-  std::unique_ptr<AutofillProfile> updated_profile =
-      table_->GetAutofillProfile(profile.guid());
-  EXPECT_TRUE(updated_profile);
-  EXPECT_EQ(ASCIIToUTF16("John"), updated_profile->GetRawInfo(NAME_FIRST));
-
-  // Try to delete the trashed profile.  This stops |RemoveAutofillProfile| from
-  // deleting it.  In normal operation deletion is done by migration step, and
-  // removal from trash is done by |WebDataService|.  |RemoveAutofillProfile|
-  // does remove the item from the trash if it is found however, so that if
-  // other clients remove it (via Sync say) then it is gone and doesn't need to
-  // be processed further by |WebDataService|.
-  EXPECT_TRUE(table_->RemoveAutofillProfile(profile.guid()));
-  std::unique_ptr<AutofillProfile> removed_profile =
-      table_->GetAutofillProfile(profile.guid());
-  EXPECT_TRUE(removed_profile);
-  EXPECT_FALSE(table_->IsAutofillGUIDInTrash(profile.guid()));
-
-  // Check that emptying the trash now allows removal to occur.
-  EXPECT_TRUE(table_->EmptyAutofillProfilesTrash());
-  EXPECT_TRUE(table_->RemoveAutofillProfile(profile.guid()));
-  removed_profile = table_->GetAutofillProfile(profile.guid());
-  EXPECT_FALSE(removed_profile);
-}
-
 TEST_F(AutofillTableTest, CreditCard) {
   // Add a 'Work' credit card.
   CreditCard work_creditcard;
diff --git a/components/autofill/core/common/form_data.cc b/components/autofill/core/common/form_data.cc
index 5c6ae60..4df9e97 100644
--- a/components/autofill/core/common/form_data.cc
+++ b/components/autofill/core/common/form_data.cc
@@ -155,15 +155,6 @@
   pickle->WriteString(form_data.main_frame_origin.Serialize());
 }
 
-void SerializeFormDataToBase64String(const FormData& form_data,
-                                     std::string* output) {
-  base::Pickle pickle;
-  SerializeFormData(form_data, &pickle);
-  Base64Encode(
-      base::StringPiece(static_cast<const char*>(pickle.data()), pickle.size()),
-      output);
-}
-
 bool DeserializeFormData(base::PickleIterator* iter, FormData* form_data) {
   int version;
   FormData temp_form_data;
@@ -227,17 +218,6 @@
   return true;
 }
 
-bool DeserializeFormDataFromBase64String(const base::StringPiece& input,
-                                         FormData* form_data) {
-  if (input.empty())
-    return false;
-  std::string pickle_data;
-  Base64Decode(input, &pickle_data);
-  base::Pickle pickle(pickle_data.data(), static_cast<int>(pickle_data.size()));
-  base::PickleIterator iter(pickle);
-  return DeserializeFormData(&iter, form_data);
-}
-
 LogBuffer& operator<<(LogBuffer& buffer, const FormData& form) {
   buffer << Tag{"div"} << Attrib{"class", "form"};
   buffer << Tag{"table"};
diff --git a/components/autofill/core/common/form_data.h b/components/autofill/core/common/form_data.h
index 2f69f52..a30f4555 100644
--- a/components/autofill/core/common/form_data.h
+++ b/components/autofill/core/common/form_data.h
@@ -118,15 +118,6 @@
 // the part of a pickle created by SerializeFormData. Returns true on success.
 bool DeserializeFormData(base::PickleIterator* iter, FormData* form_data);
 
-// Serialize FormData. Used by the PasswordManager to persist FormData
-// pertaining to password forms in base64 string. It is useful since in some
-// cases we need to store C strings without embedded '\0' symbols.
-void SerializeFormDataToBase64String(const FormData& form_data,
-                                     std::string* output);
-// Deserialize FormData. Returns true on success.
-bool DeserializeFormDataFromBase64String(const base::StringPiece& input,
-                                         FormData* form_data);
-
 LogBuffer& operator<<(LogBuffer& buffer, const FormData& form);
 
 }  // namespace autofill
diff --git a/components/autofill/core/common/form_data_unittest.cc b/components/autofill/core/common/form_data_unittest.cc
index ff36cae..eae97e5c2 100644
--- a/components/autofill/core/common/form_data_unittest.cc
+++ b/components/autofill/core/common/form_data_unittest.cc
@@ -121,7 +121,7 @@
   data->action = GURL("https://example.com/action");
   data->main_frame_origin =
       url::Origin::Create(GURL("https://origin-example.com"));
-  data->is_form_tag = true;  // Default value.
+  data->is_form_tag = true;            // Default value.
   data->is_formless_checkout = false;  // Default value.
 
   FormFieldData field_data;
@@ -166,20 +166,6 @@
   EXPECT_TRUE(actual.SameFormAs(data));
 }
 
-TEST(FormDataTest, SerializeAndDeserializeInStrings) {
-  FormData data;
-  FillInDummyFormData(&data);
-  data.is_form_tag = false;
-
-  std::string serialized_data;
-  SerializeFormDataToBase64String(data, &serialized_data);
-
-  FormData actual;
-  EXPECT_TRUE(DeserializeFormDataFromBase64String(serialized_data, &actual));
-
-  EXPECT_TRUE(actual.SameFormAs(data));
-}
-
 TEST(FormDataTest, Serialize_v1_Deserialize_vCurrent) {
   FormData data;
   FillInDummyFormData(&data);
diff --git a/components/nacl/common/nacl_service.cc b/components/nacl/common/nacl_service.cc
index 0763a6c..3cff4a6 100644
--- a/components/nacl/common/nacl_service.cc
+++ b/components/nacl/common/nacl_service.cc
@@ -12,7 +12,8 @@
 #include "content/public/common/service_names.mojom.h"
 #include "ipc/ipc.mojom.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
 #include "mojo/public/cpp/platform/platform_handle.h"
@@ -68,7 +69,7 @@
 class NaClService : public service_manager::Service {
  public:
   NaClService(service_manager::mojom::ServiceRequest request,
-              IPC::mojom::ChannelBootstrapPtrInfo bootstrap,
+              mojo::PendingRemote<IPC::mojom::ChannelBootstrap> bootstrap,
               std::unique_ptr<mojo::core::ScopedIPCSupport> ipc_support)
       : service_binding_(this, std::move(request)),
         ipc_channel_bootstrap_(std::move(bootstrap)),
@@ -83,9 +84,9 @@
     if (source_info.identity.name() == content::mojom::kSystemServiceName &&
         interface_name == IPC::mojom::ChannelBootstrap::Name_ && !connected_) {
       connected_ = true;
-      mojo::FuseInterface(
-          IPC::mojom::ChannelBootstrapRequest(std::move(interface_pipe)),
-          std::move(ipc_channel_bootstrap_));
+      mojo::FusePipes(mojo::PendingReceiver<IPC::mojom::ChannelBootstrap>(
+                          std::move(interface_pipe)),
+                      std::move(ipc_channel_bootstrap_));
     } else {
       DVLOG(1) << "Ignoring request for unknown interface " << interface_name;
     }
@@ -93,7 +94,7 @@
 
  private:
   service_manager::ServiceBinding service_binding_;
-  IPC::mojom::ChannelBootstrapPtrInfo ipc_channel_bootstrap_;
+  mojo::PendingRemote<IPC::mojom::ChannelBootstrap> ipc_channel_bootstrap_;
   std::unique_ptr<mojo::core::ScopedIPCSupport> ipc_support_;
   bool connected_ = false;
 
@@ -109,9 +110,9 @@
       std::move(io_task_runner),
       mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST);
   auto invitation = EstablishMojoConnection();
-  IPC::mojom::ChannelBootstrapPtr bootstrap;
-  *ipc_channel = mojo::MakeRequest(&bootstrap).PassMessagePipe();
+  mojo::PendingRemote<IPC::mojom::ChannelBootstrap> bootstrap;
+  *ipc_channel = bootstrap.InitWithNewPipeAndPassReceiver().PassPipe();
   return std::make_unique<NaClService>(ConnectToServiceManager(&invitation),
-                                       bootstrap.PassInterface(),
+                                       std::move(bootstrap),
                                        std::move(ipc_support));
 }
diff --git a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
index 293cdf3b..f5aa938 100644
--- a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
@@ -93,9 +93,6 @@
     logging_state_active_ = active;
   }
 
-  void AutofillUsernameAndPasswordDataReceived(
-      const autofill::FormsPredictionsMap& predictions) override {}
-
   // Records whether SetLoggingState() gets called.
   bool called_set_logging_state_;
   // Records data received via SetLoggingState() call.
diff --git a/components/password_manager/core/browser/sql_table_builder.cc b/components/password_manager/core/browser/sql_table_builder.cc
index aab889f..39cc2b1 100644
--- a/components/password_manager/core/browser/sql_table_builder.cc
+++ b/components/password_manager/core/browser/sql_table_builder.cc
@@ -172,47 +172,6 @@
                       kInvalidVersion});
 }
 
-void SQLTableBuilder::RenameIndex(const std::string& old_name,
-                                  const std::string& new_name) {
-  auto old_index = FindLastIndexByName(old_name);
-  // Check that there is an index with the old name.
-  DCHECK(old_index != indices_.rend());
-  if (old_name == new_name)  // The easy case.
-    return;
-
-  // Check that there is no index with the new name.
-  DCHECK(FindLastIndexByName(new_name) == indices_.rend());
-  // Check that there is at least one sealed version.
-  DCHECK_NE(sealed_version_, kInvalidVersion);
-  // Check that the old index has been added before the last version was sealed.
-  DCHECK_LE(old_index->min_version, sealed_version_);
-  // Check that the old index has not been renamed or deleted yet.
-  DCHECK_EQ(old_index->max_version, kInvalidVersion);
-  // This index exists in the last sealed version. Therefore it cannot be
-  // just replaced, it needs to be kept for generating the migration code.
-  old_index->max_version = sealed_version_;
-  // Add the new index.
-  indices_.push_back(
-      {new_name, old_index->columns, sealed_version_ + 1, kInvalidVersion});
-}
-
-void SQLTableBuilder::DropIndex(const std::string& name) {
-  auto index = FindLastIndexByName(name);
-  // Check that an index with the name exists.
-  DCHECK(index != indices_.rend());
-  // Check that this index exists in the last sealed version and hasn't been
-  // renamed or deleted yet.
-  // Check that there is at least one sealed version.
-  DCHECK_NE(sealed_version_, kInvalidVersion);
-  // Check that the index has been added before the last version was sealed.
-  DCHECK_LE(index->min_version, sealed_version_);
-  // Check that the index has not been renamed or deleted yet.
-  DCHECK_EQ(index->max_version, kInvalidVersion);
-  // This index exists in the last sealed version. Therefore it cannot be
-  // just deleted, it needs to be kept for generating the migration code.
-  index->max_version = sealed_version_;
-}
-
 std::string SQLTableBuilder::ComputeConstraints(unsigned version) const {
   std::string primary_key;
   std::string unique_key;
@@ -331,18 +290,6 @@
   return result;
 }
 
-std::vector<base::StringPiece> SQLTableBuilder::AllIndexNames() const {
-  DCHECK(IsVersionLastAndSealed(sealed_version_));
-  std::vector<base::StringPiece> result;
-  result.reserve(indices_.size());
-  for (const Index& index : indices_) {
-    if (IsIndexInLastVersion(index)) {
-      result.emplace_back(index.name);
-    }
-  }
-  return result;
-}
-
 size_t SQLTableBuilder::NumberOfColumns() const {
   DCHECK(IsVersionLastAndSealed(sealed_version_));
   return base::checked_cast<size_t>(std::count_if(
@@ -350,13 +297,6 @@
       [this](const Column& column) { return IsColumnInLastVersion(column); }));
 }
 
-size_t SQLTableBuilder::NumberOfIndices() const {
-  DCHECK(IsVersionLastAndSealed(sealed_version_));
-  return base::checked_cast<size_t>(std::count_if(
-      indices_.begin(), indices_.end(),
-      [this](const Index& index) { return IsIndexInLastVersion(index); }));
-}
-
 bool SQLTableBuilder::MigrateToNextFrom(unsigned old_version,
                                         sql::Database* db) const {
   DCHECK_LT(old_version, sealed_version_);
diff --git a/components/password_manager/core/browser/sql_table_builder.h b/components/password_manager/core/browser/sql_table_builder.h
index 0647cdf1..653ae21 100644
--- a/components/password_manager/core/browser/sql_table_builder.h
+++ b/components/password_manager/core/browser/sql_table_builder.h
@@ -1,6 +1,6 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
+// Copyright 2016 The Chromium Authors. All rights reserved.  Use of this source
+// code is governed by a BSD-style license that can be found in the LICENSE
+// file.
 
 #ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SQL_TABLE_BUILDER_H_
 #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SQL_TABLE_BUILDER_H_
@@ -76,15 +76,6 @@
   // referenced in |columns| must be unique and exist in the current version.
   void AddIndex(std::string name, std::vector<std::string> columns);
 
-  // Renames index |old_name| to |new_name|. |new_name| can not exist already
-  // and |old_name| must have been added in a previously sealed version, and can
-  // not have been renamed already.
-  void RenameIndex(const std::string& old_name, const std::string& new_name);
-
-  // Removes index |name|. |name| must have been added in a previously sealed
-  // version.
-  void DropIndex(const std::string& name);
-
   // Increments the internal version counter and marks the current state of the
   // table as that version. Returns the sealed version. Calling any of the
   // *Column* and *Index* methods above will result in starting a new version
@@ -121,18 +112,10 @@
   // version. The last version must be sealed.
   std::vector<base::StringPiece> AllPrimaryKeyNames() const;
 
-  // Returns a vector of all index names that are present in the last
-  // version. The last version must be sealed.
-  std::vector<base::StringPiece> AllIndexNames() const;
-
   // Returns the number of all columns present in the last version. The last
   // version must be sealed.
   size_t NumberOfColumns() const;
 
-  // Returns the number of all indices present in the last version. The last
-  // version must be sealed.
-  size_t NumberOfIndices() const;
-
  private:
   // Stores the information about one column (name, type, etc.).
   struct Column;
diff --git a/components/password_manager/core/browser/sql_table_builder_unittest.cc b/components/password_manager/core/browser/sql_table_builder_unittest.cc
index 7373a22..be440e0 100644
--- a/components/password_manager/core/browser/sql_table_builder_unittest.cc
+++ b/components/password_manager/core/browser/sql_table_builder_unittest.cc
@@ -131,22 +131,6 @@
   EXPECT_TRUE(IsColumnOfType("password_value", "BLOB"));
 }
 
-// There is no test for renaming an index in the same version, as this is a
-// misuse of the API. Instead of invoking |builder()->AddIndex("foo", ...)| and
-// |builder->RenameIndex("foo", "bar")| callers should simply use
-// |builder->AddIndex("bar", ...)|.
-
-TEST_F(SQLTableBuilderTest, RenameIndex_InNextVersion) {
-  builder()->AddIndex("old_index", {"signon_realm"});
-  EXPECT_EQ(0u, builder()->SealVersion());
-  builder()->RenameIndex("old_index", "new_index");
-  EXPECT_EQ(1u, builder()->SealVersion());
-  EXPECT_TRUE(builder()->CreateTable(db()));
-  EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
-  EXPECT_FALSE(db()->DoesIndexExist("old_index"));
-  EXPECT_TRUE(db()->DoesIndexExist("new_index"));
-}
-
 TEST_F(SQLTableBuilderTest, RenameColumn_SameNameInSameVersion) {
   builder()->AddColumn("name", "BLOB");
   builder()->RenameColumn("name", "name");
@@ -168,16 +152,6 @@
   EXPECT_TRUE(IsColumnOfType("name", "BLOB"));
 }
 
-TEST_F(SQLTableBuilderTest, RenameIndex_SameNameInNextVersion) {
-  builder()->AddIndex("my_index", {"signon_realm"});
-  EXPECT_EQ(0u, builder()->SealVersion());
-  builder()->RenameIndex("my_index", "my_index");
-  EXPECT_EQ(1u, builder()->SealVersion());
-  EXPECT_TRUE(builder()->CreateTable(db()));
-  EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
-  EXPECT_TRUE(db()->DoesIndexExist("my_index"));
-}
-
 TEST_F(SQLTableBuilderTest, DropColumn_InSameVersion) {
   builder()->AddColumn("password_value", "BLOB");
   builder()->DropColumn("password_value");
@@ -197,29 +171,17 @@
   EXPECT_FALSE(db()->DoesColumnExist("my_logins_table", "password_value"));
 }
 
-TEST_F(SQLTableBuilderTest, DropIndex_InNextVersion) {
-  builder()->AddIndex("my_index", {"signon_realm"});
-  EXPECT_EQ(0u, builder()->SealVersion());
-  builder()->DropIndex("my_index");
-  EXPECT_EQ(1u, builder()->SealVersion());
-  EXPECT_TRUE(builder()->CreateTable(db()));
-  EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
-  EXPECT_FALSE(db()->DoesIndexExist("my_index"));
-}
-
 TEST_F(SQLTableBuilderTest, MigrateFrom) {
   // First, create a table at version 0, with some columns.
   builder()->AddColumn("for_renaming", "INTEGER DEFAULT 100");
   builder()->AddColumn("for_deletion", "INTEGER");
   builder()->AddIndex("my_signon_index", {"signon_realm"});
-  builder()->AddIndex("my_changing_index_v0", {"for_renaming", "for_deletion"});
   EXPECT_EQ(0u, builder()->SealVersion());
   EXPECT_TRUE(builder()->CreateTable(db()));
   EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
   EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "for_renaming"));
   EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "for_deletion"));
   EXPECT_TRUE(db()->DoesIndexExist("my_signon_index"));
-  EXPECT_TRUE(db()->DoesIndexExist("my_changing_index_v0"));
   EXPECT_TRUE(
       db()->Execute("INSERT INTO my_logins_table (signon_realm, for_renaming, "
                     "for_deletion) VALUES ('abc', 123, 456)"));
@@ -235,7 +197,6 @@
   EXPECT_TRUE(first_check.Succeeded());
 
   // Now, specify some modifications for version 1.
-  builder()->DropIndex("my_changing_index_v0");
   builder()->RenameColumn("for_renaming", "renamed");
   builder()->DropColumn("for_deletion");
   builder()->AddColumn("new_column", "INTEGER DEFAULT 789");
@@ -255,7 +216,6 @@
   EXPECT_TRUE(IsColumnOfType("renamed", "INTEGER"));
   EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "new_column"));
   EXPECT_TRUE(db()->DoesIndexExist("my_signon_index"));
-  EXPECT_FALSE(db()->DoesIndexExist("my_changing_index_v0"));
   EXPECT_TRUE(db()->DoesIndexExist("my_changing_index_v1"));
   sql::Statement second_check(
       db()->GetCachedStatement(SQL_FROM_HERE, retrieval));
@@ -297,27 +257,6 @@
   EXPECT_THAT(builder()->AllPrimaryKeyNames(), UnorderedElementsAre("id"));
 }
 
-TEST_F(SQLTableBuilderTest, MigrateFrom_RenameAndAddIndices) {
-  builder()->AddIndex("old_name", {"signon_realm"});
-  EXPECT_EQ(0u, builder()->SealVersion());
-
-  EXPECT_TRUE(builder()->CreateTable(db()));
-
-  builder()->RenameIndex("old_name", "new_name");
-  EXPECT_EQ(1u, builder()->SealVersion());
-
-  builder()->AddIndex("added", {"signon_realm"});
-  EXPECT_EQ(2u, builder()->SealVersion());
-
-  EXPECT_TRUE(builder()->MigrateFrom(0, db()));
-  EXPECT_FALSE(db()->DoesIndexExist("old_name"));
-  EXPECT_TRUE(db()->DoesIndexExist("added"));
-  EXPECT_TRUE(db()->DoesIndexExist("new_name"));
-  EXPECT_EQ(2u, builder()->NumberOfIndices());
-  EXPECT_THAT(builder()->AllIndexNames(),
-              UnorderedElementsAre("new_name", "added"));
-}
-
 TEST_F(SQLTableBuilderTest, MigrateFrom_RenameAndAddAndDropColumns) {
   builder()->AddColumnToPrimaryKey("pk_1", "VARCHAR NOT NULL");
   builder()->AddColumnToPrimaryKey("pk_2", "VARCHAR NOT NULL");
@@ -354,29 +293,6 @@
               UnorderedElementsAre("pk_1", "pk_2"));
 }
 
-TEST_F(SQLTableBuilderTest, MigrateFrom_RenameAndAddAndDropIndices) {
-  builder()->AddIndex("old_name", {"signon_realm"});
-  EXPECT_EQ(0u, builder()->SealVersion());
-
-  EXPECT_TRUE(builder()->CreateTable(db()));
-
-  builder()->RenameIndex("old_name", "new_name");
-  EXPECT_EQ(1u, builder()->SealVersion());
-
-  builder()->AddIndex("added", {"signon_realm"});
-  EXPECT_EQ(2u, builder()->SealVersion());
-
-  builder()->DropIndex("added");
-  EXPECT_EQ(3u, builder()->SealVersion());
-
-  EXPECT_TRUE(builder()->MigrateFrom(0, db()));
-  EXPECT_FALSE(db()->DoesIndexExist("old_name"));
-  EXPECT_FALSE(db()->DoesIndexExist("added"));
-  EXPECT_TRUE(db()->DoesIndexExist("new_name"));
-  EXPECT_EQ(1u, builder()->NumberOfColumns());
-  EXPECT_THAT(builder()->AllIndexNames(), UnorderedElementsAre("new_name"));
-}
-
 TEST_F(SQLTableBuilderTest, MigrateFrom_AddPrimaryKey) {
   builder()->AddColumnToUniqueKey("uni", "VARCHAR NOT NULL");
   EXPECT_EQ(0u, builder()->SealVersion());
diff --git a/components/policy/core/common/mock_policy_service.h b/components/policy/core/common/mock_policy_service.h
index a06caa5c..5d52fa29 100644
--- a/components/policy/core/common/mock_policy_service.h
+++ b/components/policy/core/common/mock_policy_service.h
@@ -45,7 +45,6 @@
   MOCK_CONST_METHOD1(GetPolicies, const PolicyMap&(const PolicyNamespace&));
   MOCK_CONST_METHOD1(IsInitializationComplete, bool(PolicyDomain domain));
   MOCK_METHOD1(RefreshPolicies, void(const base::Closure&));
-  MOCK_METHOD1(SetInitializationThrottled, void(bool initialization_throttled));
 };
 
 }  // namespace policy
diff --git a/components/policy/core/common/policy_service.h b/components/policy/core/common/policy_service.h
index 4d04446..479165c 100644
--- a/components/policy/core/common/policy_service.h
+++ b/components/policy/core/common/policy_service.h
@@ -97,16 +97,6 @@
   // |callback| is invoked once every source has reloaded its policies, and
   // GetPolicies() is guaranteed to return the updated values at that point.
   virtual void RefreshPolicies(const base::Closure& callback) = 0;
-
-  // When |initialization_throttled| is set to true, this PolicyService should
-  // return false in IsInitializationComplete for all domains and should not
-  // notify observers that it has initialized any domain. When
-  // |initialization_throttled| is set to false and the
-  // OnPolicyServiceInitialized notifications for some domains have not been
-  // sent out due to a previous call to this function with
-  // |initialization_throttled|=true, this function will notify observers that
-  // those domains are now initializted.
-  virtual void SetInitializationThrottled(bool initialization_throttled) = 0;
 };
 
 // A registrar that only observes changes to particular policies within the
diff --git a/components/policy/core/common/policy_service_impl.cc b/components/policy/core/common/policy_service_impl.cc
index 59f8528..e508e2f 100644
--- a/components/policy/core/common/policy_service_impl.cc
+++ b/components/policy/core/common/policy_service_impl.cc
@@ -107,8 +107,14 @@
 
 }  // namespace
 
-PolicyServiceImpl::PolicyServiceImpl(Providers providers) {
-  providers_ = std::move(providers);
+PolicyServiceImpl::PolicyServiceImpl(Providers providers)
+    : PolicyServiceImpl(std::move(providers),
+                        /*initialization_throttled=*/false) {}
+
+PolicyServiceImpl::PolicyServiceImpl(Providers providers,
+                                     bool initialization_throttled)
+    : providers_(std::move(providers)),
+      initialization_throttled_(initialization_throttled) {
   for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain)
     initialization_complete_[domain] = true;
   for (auto* provider : providers_) {
@@ -123,6 +129,13 @@
   MergeAndTriggerUpdates();
 }
 
+// static
+std::unique_ptr<PolicyServiceImpl>
+PolicyServiceImpl::CreateWithThrottledInitialization(Providers providers) {
+  return base::WrapUnique(new PolicyServiceImpl(
+      std::move(providers), /*initialization_throttled=*/true));
+}
+
 PolicyServiceImpl::~PolicyServiceImpl() {
   DCHECK(thread_checker_.CalledOnValidThread());
   for (auto* provider : providers_)
@@ -177,6 +190,8 @@
 bool PolicyServiceImpl::IsInitializationComplete(PolicyDomain domain) const {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(domain >= 0 && domain < POLICY_DOMAIN_SIZE);
+  if (initialization_throttled_)
+    return false;
   return initialization_complete_[domain];
 }
 
@@ -203,12 +218,14 @@
   }
 }
 
-void PolicyServiceImpl::SetInitializationThrottled(
-    bool initialization_throttled) {
+void PolicyServiceImpl::UnthrottleInitialization() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(initialization_throttled != initialization_throttled_);
-  initialization_throttled_ = initialization_throttled;
-  CheckInitializationComplete();
+  if (!initialization_throttled_)
+    return;
+
+  initialization_throttled_ = false;
+  for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain)
+    MaybeNotifyInitializationComplete(static_cast<PolicyDomain>(domain));
 }
 
 void PolicyServiceImpl::OnUpdatePolicy(ConfigurationPolicyProvider* provider) {
@@ -355,9 +372,6 @@
 void PolicyServiceImpl::CheckInitializationComplete() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  if (initialization_throttled_)
-    return;
-
   // Check if all the providers just became initialized for each domain; if so,
   // notify that domain's observers.
   for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain) {
@@ -375,15 +389,24 @@
     }
     if (all_complete) {
       initialization_complete_[domain] = true;
-      auto iter = observers_.find(policy_domain);
-      if (iter != observers_.end()) {
-        for (auto& observer : *iter->second)
-          observer.OnPolicyServiceInitialized(policy_domain);
-      }
+      MaybeNotifyInitializationComplete(policy_domain);
     }
   }
 }
 
+void PolicyServiceImpl::MaybeNotifyInitializationComplete(
+    PolicyDomain policy_domain) {
+  if (initialization_throttled_)
+    return;
+  if (!initialization_complete_[policy_domain])
+    return;
+  auto iter = observers_.find(policy_domain);
+  if (iter != observers_.end()) {
+    for (auto& observer : *iter->second)
+      observer.OnPolicyServiceInitialized(policy_domain);
+  }
+}
+
 void PolicyServiceImpl::CheckRefreshComplete() {
   // Invoke all the callbacks if a refresh has just fully completed.
   if (refresh_pending_.empty() && !refresh_callbacks_.empty()) {
diff --git a/components/policy/core/common/policy_service_impl.h b/components/policy/core/common/policy_service_impl.h
index f1a87a7..339d6fa 100644
--- a/components/policy/core/common/policy_service_impl.h
+++ b/components/policy/core/common/policy_service_impl.h
@@ -34,6 +34,15 @@
   // Creates a new PolicyServiceImpl with the list of
   // ConfigurationPolicyProviders, in order of decreasing priority.
   explicit PolicyServiceImpl(Providers providers);
+
+  // Creates a new PolicyServiceImpl with the list of
+  // ConfigurationPolicyProviders, in order of decreasing priority.
+  // The created PolicyServiceImpl will only notify observers that
+  // initialization has completed (for any domain) after
+  // |UnthrottleInitialization| has been called.
+  static std::unique_ptr<PolicyServiceImpl> CreateWithThrottledInitialization(
+      Providers providers);
+
   ~PolicyServiceImpl() override;
 
   // PolicyService overrides:
@@ -47,12 +56,27 @@
   const PolicyMap& GetPolicies(const PolicyNamespace& ns) const override;
   bool IsInitializationComplete(PolicyDomain domain) const override;
   void RefreshPolicies(const base::Closure& callback) override;
-  void SetInitializationThrottled(bool initialization_throttled) override;
+
+  // If this PolicyServiceImpl has been created using
+  // |CreateWithThrottledInitialization|, calling UnthrottleInitialization will
+  // allow notification of observers that initialization has completed. If
+  // initialization has actually completed previously but observers were not
+  // notified yet because it was throttled, will notify observers synchronously.
+  // Has no effect if initialization was not throttled.
+  void UnthrottleInitialization();
 
  private:
   using Observers =
       base::ObserverList<PolicyService::Observer, true>::Unchecked;
 
+  // This constructor is not publicly visible so callers that want a
+  // PolicyServiceImpl with throttled initialization use
+  // |CreateWithInitializationThrottled| for clarity.
+  // If |initialization_throttled| is true, this PolicyServiceImpl will only
+  // notify observers that initialization has completed (for any domain) after
+  // |UnthrottleInitialization| has been called.
+  PolicyServiceImpl(Providers providers, bool initialization_throttled);
+
   // ConfigurationPolicyProvider::Observer overrides:
   void OnUpdatePolicy(ConfigurationPolicyProvider* provider) override;
 
@@ -68,10 +92,17 @@
   // of namespaces whose policies have been modified.
   void MergeAndTriggerUpdates();
 
-  // Checks if all providers are initialized, and notifies the observers
-  // if the service just became initialized.
+  // Checks if all providers are initialized and sets |initialization_complete_|
+  // accordingly. If initialization is not throttled, will also notify the
+  // observers if the service just became initialized.
   void CheckInitializationComplete();
 
+  // If initialization is complete for |policy_domain| and initialization is not
+  // throttled, will notify obserers for |policy_domain| that it has been
+  // initialized. This function should only be called when |policy_domain| just
+  // became initialized or when initialization has been unthrottled.
+  void MaybeNotifyInitializationComplete(PolicyDomain policy_domain);
+
   // Invokes all the refresh callbacks if there are no more refreshes pending.
   void CheckRefreshComplete();
 
@@ -110,7 +141,7 @@
   // If this is true, IsInitializationComplete should be returning false for all
   // policy domains because the owner of this PolicyService is delaying the
   // initialization signal.
-  bool initialization_throttled_ = false;
+  bool initialization_throttled_;
 
   // Used to verify thread-safe usage.
   base::ThreadChecker thread_checker_;
diff --git a/components/policy/core/common/policy_service_impl_unittest.cc b/components/policy/core/common/policy_service_impl_unittest.cc
index 2ccd9f09..5e199c1 100644
--- a/components/policy/core/common/policy_service_impl_unittest.cc
+++ b/components/policy/core/common/policy_service_impl_unittest.cc
@@ -611,7 +611,7 @@
 }
 
 TEST_F(PolicyServiceTest, IsInitializationComplete) {
-  // |provider0| has all domains initialized.
+  // |provider0_| has all domains initialized.
   Mock::VerifyAndClearExpectations(&provider1_);
   Mock::VerifyAndClearExpectations(&provider2_);
   EXPECT_CALL(provider1_, IsInitializationComplete(_))
@@ -690,7 +690,7 @@
   EXPECT_FALSE(policy_service_->IsInitializationComplete(
       POLICY_DOMAIN_SIGNIN_EXTENSIONS));
 
-  // Initialize the remaining domain.
+  // Initialize the remaining domains.
   EXPECT_CALL(observer, OnPolicyServiceInitialized(POLICY_DOMAIN_EXTENSIONS));
   EXPECT_CALL(observer,
               OnPolicyServiceInitialized(POLICY_DOMAIN_SIGNIN_EXTENSIONS));
@@ -716,6 +716,137 @@
   policy_service_->RemoveObserver(POLICY_DOMAIN_SIGNIN_EXTENSIONS, &observer);
 }
 
+// Tests initialization throttling of PolicyServiceImpl.
+// This actually tests two cases:
+// (1) A domain was initialized before UnthrottleInitialization is called.
+//     Observers only get notified after calling UntrhottleInitialization.
+//     This is tested on POLICY_DOMAIN_CHROME.
+// (2) A domain becomes initialized after UnthrottleInitialization has already
+//     been called. Because initialization is not throttled anymore, observers
+//     get notified immediately when the domain becomes initialized.
+//     This is tested on POLICY_DOMAIN_EXTENSIONS and
+//     POLICY_DOMAIN_SIGNIN_EXTENSIONS.
+TEST_F(PolicyServiceTest, InitializationThrottled) {
+  // |provider0_| and |provider1_| has all domains initialized, |provider2_| has
+  // no domain initialized.
+  Mock::VerifyAndClearExpectations(&provider2_);
+  EXPECT_CALL(provider2_, IsInitializationComplete(_))
+      .WillRepeatedly(Return(false));
+  PolicyServiceImpl::Providers providers;
+  providers.push_back(&provider0_);
+  providers.push_back(&provider1_);
+  providers.push_back(&provider2_);
+  policy_service_ = PolicyServiceImpl::CreateWithThrottledInitialization(
+      std::move(providers));
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  EXPECT_FALSE(
+      policy_service_->IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(
+      POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+
+  MockPolicyServiceObserver observer;
+  policy_service_->AddObserver(POLICY_DOMAIN_CHROME, &observer);
+  policy_service_->AddObserver(POLICY_DOMAIN_EXTENSIONS, &observer);
+  policy_service_->AddObserver(POLICY_DOMAIN_SIGNIN_EXTENSIONS, &observer);
+
+  // Now additionally initialize POLICY_DOMAIN_CHROME on |provider2_|.
+  // Note: VerifyAndClearExpectations is called to reset the previously set
+  // action for IsInitializtionComplete on |provider_2|.
+  Mock::VerifyAndClearExpectations(&provider2_);
+  EXPECT_CALL(provider2_, IsInitializationComplete(POLICY_DOMAIN_CHROME))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(provider2_, IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(provider2_,
+              IsInitializationComplete(POLICY_DOMAIN_SIGNIN_EXTENSIONS))
+      .WillRepeatedly(Return(false));
+
+  // Nothing will happen because initialization is still throttled.
+  EXPECT_CALL(observer, OnPolicyServiceInitialized(_)).Times(0);
+  const PolicyMap kPolicyMap;
+  provider2_.UpdateChromePolicy(kPolicyMap);
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  EXPECT_FALSE(
+      policy_service_->IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(
+      POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+
+  // Unthrottle initialization. This will signal that POLICY_DOMAIN_CHROME is
+  // initialized, the other domains should still not be initialized because
+  // |provider2_| is returning false in IsInitializationComplete for them.
+  EXPECT_CALL(observer, OnPolicyServiceInitialized(POLICY_DOMAIN_CHROME));
+  policy_service_->UnthrottleInitialization();
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_TRUE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  EXPECT_FALSE(
+      policy_service_->IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(
+      POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+
+  // Initialize the remaining domains.
+  // Note: VerifyAndClearExpectations is called to reset the previously set
+  // action for IsInitializtionComplete on |provider_2|.
+  Mock::VerifyAndClearExpectations(&provider2_);
+  EXPECT_CALL(provider2_, IsInitializationComplete(_))
+      .WillRepeatedly(Return(true));
+
+  EXPECT_CALL(observer, OnPolicyServiceInitialized(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_CALL(observer,
+              OnPolicyServiceInitialized(POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+  provider2_.UpdateChromePolicy(kPolicyMap);
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_TRUE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  EXPECT_TRUE(
+      policy_service_->IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_TRUE(policy_service_->IsInitializationComplete(
+      POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+
+  // Cleanup.
+  policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, &observer);
+  policy_service_->RemoveObserver(POLICY_DOMAIN_EXTENSIONS, &observer);
+  policy_service_->RemoveObserver(POLICY_DOMAIN_SIGNIN_EXTENSIONS, &observer);
+}
+
+TEST_F(PolicyServiceTest, InitializationThrottledProvidersAlreadyInitialized) {
+  // All providers have all domains initialized.
+  PolicyServiceImpl::Providers providers;
+  providers.push_back(&provider0_);
+  providers.push_back(&provider1_);
+  providers.push_back(&provider2_);
+  policy_service_ = PolicyServiceImpl::CreateWithThrottledInitialization(
+      std::move(providers));
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  EXPECT_FALSE(
+      policy_service_->IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(
+      POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+
+  MockPolicyServiceObserver observer;
+  policy_service_->AddObserver(POLICY_DOMAIN_CHROME, &observer);
+  policy_service_->AddObserver(POLICY_DOMAIN_EXTENSIONS, &observer);
+  policy_service_->AddObserver(POLICY_DOMAIN_SIGNIN_EXTENSIONS, &observer);
+
+  // Unthrottle initialization. This will signal that all domains are
+  // initialized.
+  EXPECT_CALL(observer, OnPolicyServiceInitialized(POLICY_DOMAIN_CHROME));
+  EXPECT_CALL(observer, OnPolicyServiceInitialized(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_CALL(observer,
+              OnPolicyServiceInitialized(POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+  policy_service_->UnthrottleInitialization();
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_TRUE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  EXPECT_TRUE(
+      policy_service_->IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_TRUE(policy_service_->IsInitializationComplete(
+      POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+
+  // Cleanup.
+  policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, &observer);
+  policy_service_->RemoveObserver(POLICY_DOMAIN_EXTENSIONS, &observer);
+  policy_service_->RemoveObserver(POLICY_DOMAIN_SIGNIN_EXTENSIONS, &observer);
+}
+
 TEST_F(PolicyServiceTest, SeparateProxyPoliciesMerging) {
   const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
   const PolicyNamespace extension_namespace(POLICY_DOMAIN_EXTENSIONS, "xyz");
diff --git a/components/signin/internal/identity_manager/account_info_fetcher.h b/components/signin/internal/identity_manager/account_info_fetcher.h
index c43ba6db..c981f39 100644
--- a/components/signin/internal/identity_manager/account_info_fetcher.h
+++ b/components/signin/internal/identity_manager/account_info_fetcher.h
@@ -33,8 +33,6 @@
       const CoreAccountId& account_id);
   ~AccountInfoFetcher() override;
 
-  const CoreAccountId& account_id() { return account_id_; }
-
   // Start fetching the account information.
   void Start();
 
diff --git a/components/signin/public/base/signin_metrics.cc b/components/signin/public/base/signin_metrics.cc
index 64a04cb..14e1052 100644
--- a/components/signin/public/base/signin_metrics.cc
+++ b/components/signin/public/base/signin_metrics.cc
@@ -742,11 +742,6 @@
   }
 }
 
-void LogSigninConfirmHistogramValue(ConfirmationUsage action) {
-  UMA_HISTOGRAM_ENUMERATION("Signin.OneClickConfirmation", action,
-                            HISTOGRAM_CONFIRM_MAX);
-}
-
 void LogAccountReconcilorStateOnGaiaResponse(AccountReconcilorState state) {
   UMA_HISTOGRAM_ENUMERATION("Signin.AccountReconcilorState.OnGaiaResponse",
                             state, ACCOUNT_RECONCILOR_HISTOGRAM_COUNT);
diff --git a/components/signin/public/base/signin_metrics.h b/components/signin/public/base/signin_metrics.h
index 7d2ada0..cfb5122 100644
--- a/components/signin/public/base/signin_metrics.h
+++ b/components/signin/public/base/signin_metrics.h
@@ -378,8 +378,6 @@
 // Track when the current authentication error changed.
 void LogAuthError(const GoogleServiceAuthError& auth_error);
 
-void LogSigninConfirmHistogramValue(ConfirmationUsage action);
-
 // Records the AccountReconcilor |state| when GAIA returns a specific response.
 // If |state| is different than ACCOUNT_RECONCILOR_OK it means the user will
 // be shown a different set of accounts in the content-area and the settings UI.
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 12ff5a6..18bb2c2 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1862,6 +1862,8 @@
     "web_package/bundled_exchanges_source.h",
     "web_package/bundled_exchanges_url_loader_factory.cc",
     "web_package/bundled_exchanges_url_loader_factory.h",
+    "web_package/bundled_exchanges_utils.cc",
+    "web_package/bundled_exchanges_utils.h",
     "web_package/prefetched_signed_exchange_cache.cc",
     "web_package/prefetched_signed_exchange_cache.h",
     "web_package/prefetched_signed_exchange_cache_adapter.cc",
diff --git a/content/browser/android/content_url_loader_factory.cc b/content/browser/android/content_url_loader_factory.cc
index d3ebb18..52f1d381 100644
--- a/content/browser/android/content_url_loader_factory.cc
+++ b/content/browser/android/content_url_loader_factory.cc
@@ -15,6 +15,7 @@
 #include "base/files/file_util.h"
 #include "base/macros.h"
 #include "base/time/time.h"
+#include "content/browser/web_package/bundled_exchanges_utils.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/file_url_loader.h"
 #include "content/public/common/content_client.h"
@@ -160,6 +161,32 @@
 
     // Set the mimetype of the response.
     GetMimeType(request, path, &head.mime_type);
+    if (head.mime_type.empty() ||
+        head.mime_type == "application/octet-stream") {
+      // When a bundled exchanges file is downloaded with
+      // "content-type: application/webbundle;v=b1" header, Chrome saves it as
+      // "application/webbundle" MIME type. The MIME type is stored to Android's
+      // DownloadManager. If the file is opened from a URI which is under
+      // DownloadManager's control and the ContentProvider can get the MIME
+      // type, |head.mime_type| is set to "application/webbundle". But otherwise
+      // ContentResolver.getType() returns null or the default type
+      // [1]"application/octet-stream" even if the file extension is ".wbn".
+      // (eg: opening the file from "Internal Storage")
+      // This is because the Media type of BundledHTTPExchanges isn't registered
+      // to the IANA registry (https://www.iana.org/assignments/media-types/),
+      // and it is not listed in the mime.types files [2][3] which was referd by
+      // MimeTypeMap.getMimeTypeFromExtension().
+      // So we set the MIME type if the file extension is ".wbn" by calling
+      // bundled_exchanges_utils::GetBundledExchangesFileMimeTypeFromFile().
+      // [1]
+      // https://android.googlesource.com/platform/frameworks/base/+/1b817f6/core/java/android/content/ContentResolver.java#481
+      // [2] https://android.googlesource.com/platform/external/mime-support/
+      // [3]
+      // https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/libcore/net/android.mime.types
+      bundled_exchanges_utils::GetBundledExchangesFileMimeTypeFromFile(
+          path, &head.mime_type);
+    }
+
     if (!head.mime_type.empty()) {
       head.headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
       head.headers->AddHeader(
diff --git a/content/browser/appcache/appcache_request_handler.cc b/content/browser/appcache/appcache_request_handler.cc
index 248005a..350ad36 100644
--- a/content/browser/appcache/appcache_request_handler.cc
+++ b/content/browser/appcache/appcache_request_handler.cc
@@ -543,7 +543,8 @@
     network::mojom::URLLoaderPtr* loader,
     network::mojom::URLLoaderClientRequest* client_request,
     ThrottlingURLLoader* url_loader,
-    bool* skip_other_interceptors) {
+    bool* skip_other_interceptors,
+    bool* will_return_unsafe_redirect) {
   // The sync interface of this method is inherited from the
   // NavigationLoaderInterceptor class. The LoaderCallback created here is
   // invoked synchronously in fallback cases, and only when there really is
diff --git a/content/browser/appcache/appcache_request_handler.h b/content/browser/appcache/appcache_request_handler.h
index 6d095b2..964b796 100644
--- a/content/browser/appcache/appcache_request_handler.h
+++ b/content/browser/appcache/appcache_request_handler.h
@@ -72,7 +72,8 @@
       network::mojom::URLLoaderPtr* loader,
       network::mojom::URLLoaderClientRequest* client_request,
       ThrottlingURLLoader* url_loader,
-      bool* skip_other_interceptors) override;
+      bool* skip_other_interceptors,
+      bool* will_return_unsafe_redirect) override;
   base::Optional<SubresourceLoaderParams> MaybeCreateSubresourceLoaderParams()
       override;
 
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 3636040..7833506 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -764,6 +764,31 @@
   delete_observer_rfh_a.WaitUntilDeleted();
 }
 
+// TODO(https://crbug.com/154571): Shared workers are not available on Android.
+#if defined(OS_ANDROID)
+#define MAYBE_PageWithSharedWorkerNotCached \
+  DISABLED_PageWithSharedWorkerNotCached
+#else
+#define MAYBE_PageWithSharedWorkerNotCached PageWithSharedWorkerNotCached
+#endif
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       MAYBE_PageWithSharedWorkerNotCached) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  EXPECT_TRUE(NavigateToURL(
+      shell(),
+      embedded_test_server()->GetURL(
+          "a.com", "/back_forward_cache/page_with_shared_worker.html")));
+  RenderFrameDeletedObserver delete_observer_rfh_a(current_frame_host());
+
+  // Navigate away.
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
+
+  // The page with the unsupported feature should be deleted (not cached).
+  delete_observer_rfh_a.WaitUntilDeleted();
+}
+
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
                        SubframeWithDisallowedFeatureNotCached) {
   ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 923f0b5..55b4a1ac 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -9,6 +9,8 @@
 #include "content/browser/content_index/content_index_service_impl.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/image_capture/image_capture_impl.h"
+#include "content/browser/keyboard_lock/keyboard_lock_service_impl.h"
+#include "content/browser/picture_in_picture/picture_in_picture_service_impl.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/screen_enumeration/screen_enumeration_impl.h"
 #include "content/browser/service_worker/service_worker_provider_host.h"
@@ -29,8 +31,10 @@
 #include "third_party/blink/public/mojom/credentialmanager/credential_manager.mojom.h"
 #include "third_party/blink/public/mojom/filesystem/file_system.mojom.h"
 #include "third_party/blink/public/mojom/idle/idle_manager.mojom.h"
+#include "third_party/blink/public/mojom/keyboard_lock/keyboard_lock.mojom.h"
 #include "third_party/blink/public/mojom/locks/lock_manager.mojom.h"
 #include "third_party/blink/public/mojom/permissions/permission.mojom.h"
+#include "third_party/blink/public/mojom/picture_in_picture/picture_in_picture.mojom.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
 #include "third_party/blink/public/mojom/speech/speech_synthesis.mojom.h"
 #include "third_party/blink/public/mojom/webaudio/audio_context_manager.mojom.h"
@@ -130,6 +134,10 @@
       base::BindRepeating(&BackgroundFetchServiceImpl::CreateForFrame));
   map->Add<blink::mojom::ContentIndexService>(
       base::BindRepeating(&ContentIndexServiceImpl::CreateForFrame));
+  map->Add<blink::mojom::KeyboardLockService>(
+      base::BindRepeating(&KeyboardLockServiceImpl::CreateMojoService));
+  map->Add<blink::mojom::PictureInPictureService>(
+      base::BindRepeating(&PictureInPictureServiceImpl::Create));
   GetContentClient()->browser()->RegisterBrowserInterfaceBindersForFrame(map);
 }
 
diff --git a/content/browser/browsing_data/browsing_data_remover_impl.cc b/content/browser/browsing_data/browsing_data_remover_impl.cc
index f5da221..32b4751a 100644
--- a/content/browser/browsing_data/browsing_data_remover_impl.cc
+++ b/content/browser/browsing_data/browsing_data_remover_impl.cc
@@ -360,7 +360,12 @@
     storage_partition_remove_mask |=
         StoragePartition::REMOVE_DATA_MASK_BACKGROUND_FETCH;
   }
-
+  if (remove_mask & DATA_TYPE_CACHE) {
+    // Tell the shader disk cache to clear.
+    base::RecordAction(UserMetricsAction("ClearBrowsingData_ShaderCache"));
+    storage_partition_remove_mask |=
+        StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE;
+  }
   // Content Decryption Modules used by Encrypted Media store licenses in a
   // private filesystem. These are different than content licenses used by
   // Flash (which are deleted father down in this method).
@@ -447,11 +452,6 @@
 
     // Clears the PrefetchedSignedExchangeCache of all RenderFrameHostImpls.
     RenderFrameHostImpl::ClearAllPrefetchedSignedExchangeCache();
-
-    // Tell the shader disk cache to clear.
-    base::RecordAction(UserMetricsAction("ClearBrowsingData_ShaderCache"));
-    storage_partition_remove_mask |=
-        StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE;
   }
 
 #if BUILDFLAG(ENABLE_REPORTING)
diff --git a/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc b/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc
index 6c1db542..d31cb4f0 100644
--- a/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc
+++ b/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc
@@ -1215,6 +1215,14 @@
   EXPECT_TRUE(removal_data.remove_code_cache);
 }
 
+TEST_F(BrowsingDataRemoverImplTest, RemoveShaderCache) {
+  BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(),
+                                BrowsingDataRemover::DATA_TYPE_CACHE, false);
+  StoragePartitionRemovalData removal_data = GetStoragePartitionRemovalData();
+  EXPECT_EQ(removal_data.remove_mask,
+            StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE);
+}
+
 class MultipleTasksObserver {
  public:
   // A simple implementation of BrowsingDataRemover::Observer.
diff --git a/content/browser/file_url_loader_factory.cc b/content/browser/file_url_loader_factory.cc
index 7ba913f..4fedf44 100644
--- a/content/browser/file_url_loader_factory.cc
+++ b/content/browser/file_url_loader_factory.cc
@@ -24,6 +24,7 @@
 #include "base/task/post_task.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "content/browser/web_package/bundled_exchanges_utils.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
@@ -633,7 +634,11 @@
       total_bytes_to_send -= write_size;
     }
 
-    if (!net::GetMimeTypeFromFile(path, &head.mime_type)) {
+    // TODO(crbug.com/995177): Update mime_util.cc when BundledHTTPExchanges is
+    // launched and stop using GetBundledExchangesFileMimeTypeFromFile().
+    if (!bundled_exchanges_utils::GetBundledExchangesFileMimeTypeFromFile(
+            path, &head.mime_type) &&
+        !net::GetMimeTypeFromFile(path, &head.mime_type)) {
       std::string new_type;
       net::SniffMimeType(
           initial_read_buffer.data(), read_result.bytes_read, request.url,
diff --git a/content/browser/frame_host/back_forward_cache_impl.cc b/content/browser/frame_host/back_forward_cache_impl.cc
index 118adcf..9cd6e1c 100644
--- a/content/browser/frame_host/back_forward_cache_impl.cc
+++ b/content/browser/frame_host/back_forward_cache_impl.cc
@@ -31,7 +31,7 @@
 
 // Converts a WebSchedulerTrackedFeature to a bit for use in a bitmask.
 constexpr uint64_t ToFeatureBit(WebSchedulerTrackedFeature feature) {
-  return 1 << static_cast<uint32_t>(feature);
+  return 1ull << static_cast<uint32_t>(feature);
 }
 
 void SetPageFrozenImpl(
@@ -95,6 +95,8 @@
       ToFeatureBit(WebSchedulerTrackedFeature::kIndexedDBConnection) |
       ToFeatureBit(WebSchedulerTrackedFeature::kWebGL) |
       ToFeatureBit(WebSchedulerTrackedFeature::kWebVR) |
+      ToFeatureBit(WebSchedulerTrackedFeature::kWebXR) |
+      ToFeatureBit(WebSchedulerTrackedFeature::kSharedWorker) |
       ToFeatureBit(WebSchedulerTrackedFeature::kWebXR);
 
   uint64_t result = kAlwaysDisallowedFeatures;
diff --git a/content/browser/frame_host/back_forward_cache_metrics_browsertest.cc b/content/browser/frame_host/back_forward_cache_metrics_browsertest.cc
index ab3d585..da94433 100644
--- a/content/browser/frame_host/back_forward_cache_metrics_browsertest.cc
+++ b/content/browser/frame_host/back_forward_cache_metrics_browsertest.cc
@@ -497,6 +497,26 @@
                                       kDedicatedWorkerOrWorklet));
 }
 
+// TODO(https://crbug.com/154571): Shared workers are not available on Android.
+#if defined(OS_ANDROID)
+#define MAYBE_SharedWorker DISABLED_SharedWorker
+#else
+#define MAYBE_SharedWorker SharedWorker
+#endif
+IN_PROC_BROWSER_TEST_F(BackForwardCacheMetricsBrowserTest, MAYBE_SharedWorker) {
+  const GURL url(embedded_test_server()->GetURL(
+      "/back_forward_cache/page_with_shared_worker.html"));
+
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+
+  EXPECT_EQ(static_cast<WebContentsImpl*>(shell()->web_contents())
+                    ->GetMainFrame()
+                    ->scheduler_tracked_features() &
+                ~kFeaturesToIgnoreMask,
+            1ull << static_cast<uint32_t>(
+                blink::scheduler::WebSchedulerTrackedFeature::kSharedWorker));
+}
+
 IN_PROC_BROWSER_TEST_F(BackForwardCacheMetricsBrowserTest,
                        WindowOpen_SameOrigin) {
   ukm::TestAutoSetUkmRecorder recorder;
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index aacc5cc..b0b5ee8 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -3126,7 +3126,9 @@
           std::string(), /* data_url_as_string */
 #endif
           false, /* is_browser_initiated */
-          network::mojom::IPAddressSpace::kUnknown);
+          network::mojom::IPAddressSpace::kUnknown,
+          GURL() /* base_url_override_for_bundled_exchanges */
+      );
 #if defined(OS_ANDROID)
   if (ValidateDataURLAsString(params.data_url_as_string)) {
     commit_params->data_url_as_string = params.data_url_as_string->data();
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc
index dce0551..2dddb36 100644
--- a/content/browser/frame_host/navigation_entry_impl.cc
+++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -766,7 +766,8 @@
 #if defined(OS_ANDROID)
           std::string(),
 #endif
-          false, network::mojom::IPAddressSpace::kUnknown);
+          false, network::mojom::IPAddressSpace::kUnknown,
+          GURL() /* base_url_override_for_bundled_exchanges */);
 #if defined(OS_ANDROID)
   if (NavigationControllerImpl::ValidateDataURLAsString(GetDataURLAsString())) {
     commit_params->data_url_as_string = GetDataURLAsString()->data();
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 61336b0..25d718a 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -49,6 +49,8 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_navigation_handle.h"
 #include "content/browser/site_instance_impl.h"
+#include "content/browser/web_package/bundled_exchanges_source.h"
+#include "content/browser/web_package/bundled_exchanges_utils.h"
 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
 #include "content/common/appcache_interfaces.h"
 #include "content/common/content_constants_internal.h"
@@ -730,7 +732,9 @@
           std::string(),  // data_url_as_string
 #endif
           false,  // is_browser_initiated
-          network::mojom::IPAddressSpace::kUnknown);
+          network::mojom::IPAddressSpace::kUnknown,
+          GURL() /* base_url_override_for_bundled_exchanges */
+      );
   std::unique_ptr<NavigationRequest> navigation_request(new NavigationRequest(
       frame_tree_node, std::move(common_params), std::move(begin_params),
       std::move(commit_params),
@@ -801,7 +805,9 @@
           std::string(), /* data_url_as_string */
 #endif
           false,  // is_browser_initiated
-          network::mojom::IPAddressSpace::kUnknown);
+          network::mojom::IPAddressSpace::kUnknown,
+          GURL() /* base_url_override_for_bundled_exchanges */
+      );
   mojom::BeginNavigationParamsPtr begin_params =
       mojom::BeginNavigationParams::New();
   std::unique_ptr<NavigationRequest> navigation_request(new NavigationRequest(
@@ -1972,23 +1978,19 @@
   }
 
   // Initialize the BundledExchangesHandle.
-  if (GetContentClient()->browser()->CanAcceptUntrustedExchangesIfNeeded() &&
-      (common_params_->url.SchemeIsFile() ||
-       common_params_->url.SchemeIs(url::kContentScheme)) &&
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kTrustableBundledExchangesFileUrl)) {
-    // Fast path for testing navigation to a trustable BundledExchanges source.
-    const std::string specified_trustable_wbn_url =
-        base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-            switches::kTrustableBundledExchangesFileUrl);
-    if (specified_trustable_wbn_url == common_params_->url.spec()) {
-      bundled_exchanges_handle_ = std::make_unique<BundledExchangesHandle>(
-          BundledExchangesSource::CreateFromTrustedFileUrl(
-              GURL(specified_trustable_wbn_url)));
+  if (bundled_exchanges_utils::CanLoadAsTrustableBundledExchangesFile(
+          common_params_->url)) {
+    auto source = BundledExchangesSource::MaybeCreateFromTrustedFileUrl(
+        common_params_->url);
+    // MaybeCreateFromTrustedFileUrl() returns null when the url contains an
+    // invalid character.
+    if (source) {
+      bundled_exchanges_handle_ =
+          BundledExchangesHandle::CreateForTrustableFile(std::move(source));
     }
-  } else if (base::FeatureList::IsEnabled(features::kBundledHTTPExchanges)) {
-    // Production path behind the feature flag.
-    bundled_exchanges_handle_ = std::make_unique<BundledExchangesHandle>();
+  } else if (bundled_exchanges_utils::CanLoadAsBundledExchangesFile(
+                 common_params_->url)) {
+    bundled_exchanges_handle_ = BundledExchangesHandle::CreateForFile();
   }
 
   // Mark the fetch_start (Navigation Timing API).
@@ -2036,7 +2038,7 @@
   // ResourceRequest directly here.
   std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptor;
   if (bundled_exchanges_handle_)
-    interceptor.push_back(bundled_exchanges_handle_->CreateInterceptor());
+    interceptor.push_back(bundled_exchanges_handle_->TakeInterceptor());
   loader_ = NavigationURLLoader::Create(
       browser_context, partition,
       std::make_unique<NavigationRequestInfo>(
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index cabc883e..b6f41af3 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -67,7 +67,6 @@
 #include "content/browser/geolocation/geolocation_service_impl.h"
 #include "content/browser/installedapp/installed_app_provider_impl_default.h"
 #include "content/browser/interface_provider_filtering.h"
-#include "content/browser/keyboard_lock/keyboard_lock_service_impl.h"
 #include "content/browser/loader/navigation_url_loader_impl.h"
 #include "content/browser/loader/prefetch_url_loader_service.h"
 #include "content/browser/log_console_message.h"
@@ -81,7 +80,6 @@
 #include "content/browser/permissions/permission_controller_impl.h"
 #include "content/browser/permissions/permission_service_context.h"
 #include "content/browser/permissions/permission_service_impl.h"
-#include "content/browser/picture_in_picture/picture_in_picture_service_impl.h"
 #include "content/browser/portal/portal.h"
 #include "content/browser/presentation/presentation_service_impl.h"
 #include "content/browser/push_messaging/push_messaging_manager.h"
@@ -2163,19 +2161,30 @@
   if (frame_tree_node_->IsMainFrame())
     return destination;
 
-  GURL base_url;
+  // Check if everything above the frame being navigated is consistent. It's OK
+  // to skip checking the frame itself since it will be validated against
+  // |site_for_cookies| anyway.
+  return ComputeSiteForCookiesInternal(parent_);
+}
+
+GURL RenderFrameHostImpl::ComputeSiteForCookies() const {
+  return ComputeSiteForCookiesInternal(this);
+}
+
+GURL RenderFrameHostImpl::ComputeSiteForCookiesInternal(
+    const RenderFrameHostImpl* render_frame_host) const {
 #if defined(OS_ANDROID)
   // On Android, a base URL can be set for the frame. If this the case, it is
   // the URL to use for cookies.
   NavigationEntry* last_committed_entry =
       frame_tree_node_->navigator()->GetController()->GetLastCommittedEntry();
-  if (last_committed_entry)
-    base_url = last_committed_entry->GetBaseURLForDataURL();
+  if (last_committed_entry &&
+      !last_committed_entry->GetBaseURLForDataURL().is_empty()) {
+    return last_committed_entry->GetBaseURLForDataURL();
+  }
 #endif
-  // This is pre-navigation, but since at this point the frame being navigated
-  // is known to not be the main frame, it's correct post-navigation as well.
-  const GURL& top_document_url =
-      !base_url.is_empty() ? base_url : frame_tree_->root()->current_url();
+
+  const GURL& top_document_url = frame_tree_->root()->current_url();
 
   if (GetContentClient()
           ->browser()
@@ -2184,23 +2193,18 @@
     return top_document_url;
   }
 
-  // Check if everything above the frame being navigated is consistent. It's OK
-  // to skip checking the frame itself since it will be validated against
-  // |site_for_cookies| anyway.
-  const FrameTreeNode* current = frame_tree_node_->parent();
-  bool ancestors_are_same_site = true;
-  while (current && ancestors_are_same_site) {
+  // Make sure every ancestors are same-domain with the main document. Otherwise
+  // this will be a 3rd party cookie.
+  for (const RenderFrameHostImpl* rfh = render_frame_host; rfh;
+       rfh = rfh->parent_) {
     if (!net::registry_controlled_domains::SameDomainOrHost(
-            top_document_url,
-            current->current_frame_host()->GetLastCommittedOrigin(),
+            top_document_url, rfh->last_committed_origin_,
             net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-      ancestors_are_same_site = false;
+      return GURL::EmptyGURL();
     }
-    current = current->parent();
   }
 
-  return (ancestors_are_same_site || !base_url.is_empty()) ? top_document_url
-                                                           : GURL::EmptyGURL();
+  return top_document_url;
 }
 
 void RenderFrameHostImpl::SetOriginOfNewFrame(
@@ -4393,9 +4397,6 @@
                                      GetProcess()->GetID(), GetRoutingID()));
 #endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
 
-  registry_->AddInterface(base::BindRepeating(
-      &KeyboardLockServiceImpl::CreateMojoService, base::Unretained(this)));
-
 #if !defined(OS_ANDROID)
   if (command_line->HasSwitch(
           switches::kEnableExperimentalWebPlatformFeatures)) {
@@ -4472,9 +4473,6 @@
   registry_->AddInterface(base::BindRepeating(&WakeLockServiceImpl::Create,
                                               base::Unretained(this)));
 
-  registry_->AddInterface(base::BindRepeating(
-      &PictureInPictureServiceImpl::Create, base::Unretained(this)));
-
   if (base::FeatureList::IsEnabled(blink::features::kNativeFileSystemAPI)) {
     registry_->AddInterface(base::BindRepeating(
         [](RenderFrameHostImpl* frame,
@@ -5209,6 +5207,10 @@
       bundled_exchanges_handle_->CreateURLLoaderFactory(
           pending_default_factory.InitWithNewPipeAndPassReceiver(),
           std::move(fallback_factory));
+      if (bundled_exchanges_handle_->base_url_override().is_valid()) {
+        commit_params->base_url_override_for_bundled_exchanges =
+            bundled_exchanges_handle_->base_url_override();
+      }
     }
 
     DCHECK(pending_default_factory);
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 3337eca..728e5f9 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -465,6 +465,11 @@
   // |destination|.
   GURL ComputeSiteForCookiesForNavigation(const GURL& destination) const;
 
+  // Computes site_for_cookies for this frame. It can be used to check
+  // if cookies (including storage APIs and shared/service workers) are
+  // accessible.
+  GURL ComputeSiteForCookies() const;
+
   // Allows overriding the last committed origin in tests.
   void SetLastCommittedOriginForTesting(const url::Origin& origin);
 
@@ -1360,6 +1365,12 @@
   void OnFrameDidCallFocus();
   void OnRenderFallbackContentInParentProcess();
 
+  // To be called by ComputeSiteForCookiesForNavigation() and
+  // ComputeSiteForCookies().
+  // Starts traversing the tree from |render_frame_host|.
+  GURL ComputeSiteForCookiesInternal(
+      const RenderFrameHostImpl* render_frame_host) const;
+
 #if BUILDFLAG(USE_EXTERNAL_POPUP_MENU)
   void OnShowPopup(const FrameHostMsg_ShowPopup_Params& params);
   void OnHidePopup();
diff --git a/content/browser/loader/navigation_loader_interceptor.cc b/content/browser/loader/navigation_loader_interceptor.cc
index 22fc4ef..d2a4b810 100644
--- a/content/browser/loader/navigation_loader_interceptor.cc
+++ b/content/browser/loader/navigation_loader_interceptor.cc
@@ -20,7 +20,8 @@
     network::mojom::URLLoaderPtr* loader,
     network::mojom::URLLoaderClientRequest* client_request,
     ThrottlingURLLoader* url_loader,
-    bool* skip_other_interceptors) {
+    bool* skip_other_interceptors,
+    bool* will_return_unsafe_redirect) {
   return false;
 }
 
diff --git a/content/browser/loader/navigation_loader_interceptor.h b/content/browser/loader/navigation_loader_interceptor.h
index 95a240d..5796a05 100644
--- a/content/browser/loader/navigation_loader_interceptor.h
+++ b/content/browser/loader/navigation_loader_interceptor.h
@@ -107,6 +107,10 @@
   // flag was introduced to skip service worker after signed exchange redirect.
   // Remove this flag when we support service worker and signed exchange
   // integration. See crbug.com/894755#c1. Nullptr is not allowed.
+  // |will_return_unsafe_redirect| is set to true when this interceptor will
+  // return an unsafe redirect response and will handle the redirected request,
+  // therefore regular safety check should be exempted for the redirect.
+  // Nullptr is not allowed.
   virtual bool MaybeCreateLoaderForResponse(
       const network::ResourceRequest& request,
       const network::ResourceResponseHead& response_head,
@@ -114,7 +118,8 @@
       network::mojom::URLLoaderPtr* loader,
       network::mojom::URLLoaderClientRequest* client_request,
       ThrottlingURLLoader* url_loader,
-      bool* skip_other_interceptors);
+      bool* skip_other_interceptors,
+      bool* will_return_unsafe_redirect);
 };
 
 }  // namespace content
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index a3a266e..adbfc2c 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -39,6 +39,7 @@
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/browser/web_package/bundled_exchanges_utils.h"
 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
 #include "content/browser/web_package/signed_exchange_consts.h"
 #include "content/browser/web_package/signed_exchange_request_handler.h"
@@ -1021,17 +1022,23 @@
   // different response. For e.g. AppCache may have fallback content.
   bool MaybeCreateLoaderForResponse(
       const network::ResourceResponseHead& response) {
-    if (!default_loader_used_)
+    if (!default_loader_used_ &&
+        !bundled_exchanges_utils::CanLoadAsBundledExchanges(
+            url_, response.mime_type)) {
       return false;
-
+    }
     for (size_t i = 0u; i < interceptors_.size(); ++i) {
       NavigationLoaderInterceptor* interceptor = interceptors_[i].get();
       network::mojom::URLLoaderClientRequest response_client_request;
       bool skip_other_interceptors = false;
+      bool will_return_unsafe_redirect = false;
       if (interceptor->MaybeCreateLoaderForResponse(
               *resource_request_, response, &response_body_,
               &response_url_loader_, &response_client_request,
-              url_loader_.get(), &skip_other_interceptors)) {
+              url_loader_.get(), &skip_other_interceptors,
+              &will_return_unsafe_redirect)) {
+        if (will_return_unsafe_redirect)
+          bypass_redirect_checks_ = true;
         if (response_loader_binding_.is_bound())
           response_loader_binding_.Close();
         response_loader_binding_.Bind(std::move(response_client_request));
@@ -1194,7 +1201,9 @@
   // protocol handlers.
   std::set<std::string> known_schemes_;
 
-  // If true, redirect checks will be handled in a proxy, and not here.
+  // True when a proxy will handle the redirect checks, or when an interceptor
+  // intentionally returned unsafe redirect response
+  // (eg: NavigationLoaderInterceptor for loading local bundled exchanges file).
   bool bypass_redirect_checks_;
 
   // Used to reset the state of ServiceWorkerProviderHost when
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc
index 08d8baa..f889eb0 100644
--- a/content/browser/loader/navigation_url_loader_impl_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -106,7 +106,8 @@
       network::mojom::URLLoaderPtr* loader,
       network::mojom::URLLoaderClientRequest* client_request,
       ThrottlingURLLoader* url_loader,
-      bool* skip_other_interceptors) override {
+      bool* skip_other_interceptors,
+      bool* will_return_unsafe_redirect) override {
     return false;
   }
 
diff --git a/content/browser/renderer_host/media/peer_connection_tracker_host.cc b/content/browser/renderer_host/media/peer_connection_tracker_host.cc
index 75453f0d..5048f9c 100644
--- a/content/browser/renderer_host/media/peer_connection_tracker_host.cc
+++ b/content/browser/renderer_host/media/peer_connection_tracker_host.cc
@@ -24,8 +24,6 @@
   bool handled = true;
 
   IPC_BEGIN_MESSAGE_MAP(PeerConnectionTrackerHost, message)
-    IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_AddPeerConnection,
-                        OnAddPeerConnection)
     IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_AddStandardStats,
                         OnAddStandardStats)
     IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_AddLegacyStats,
@@ -61,20 +59,27 @@
   base::PowerMonitor::RemoveObserver(this);
 }
 
-void PeerConnectionTrackerHost::OnAddPeerConnection(
-    const PeerConnectionInfo& info) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+void PeerConnectionTrackerHost::AddPeerConnection(
+    mojom::PeerConnectionInfoPtr info) {
+  // TODO(crbug.com/787254): Remove the thread jump on all the methods
+  // here, and make sure the mojo pipe is bound on the proper thread instead.
+  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+    base::PostTask(FROM_HERE, {BrowserThread::UI},
+                   base::BindOnce(&PeerConnectionTrackerHost::AddPeerConnection,
+                                  this, std::move(info)));
+    return;
+  }
 
   WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
   if (webrtc_internals) {
     webrtc_internals->OnAddPeerConnection(
-        render_process_id_, peer_pid(), info.lid, info.url,
-        info.rtc_configuration, info.constraints);
+        render_process_id_, peer_pid(), info->lid, info->url,
+        info->rtc_configuration, info->constraints);
   }
 
   WebRtcEventLogger* const logger = WebRtcEventLogger::Get();
   if (logger) {
-    logger->PeerConnectionAdded(render_process_id_, info.lid,
+    logger->PeerConnectionAdded(render_process_id_, info->lid,
                                 base::OnceCallback<void(bool)>());
   }
 }
diff --git a/content/browser/renderer_host/media/peer_connection_tracker_host.h b/content/browser/renderer_host/media/peer_connection_tracker_host.h
index e1533b3..62a5f60 100644
--- a/content/browser/renderer_host/media/peer_connection_tracker_host.h
+++ b/content/browser/renderer_host/media/peer_connection_tracker_host.h
@@ -14,8 +14,6 @@
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/browser_thread.h"
 
-struct PeerConnectionInfo;
-
 namespace base {
 class ListValue;
 }  // namespace base
@@ -49,12 +47,12 @@
 
  private:
   // Handlers for IPC messages coming from the renderer.
-  void OnAddPeerConnection(const PeerConnectionInfo& info);
   void OnAddStandardStats(int lid, const base::ListValue& value);
   void OnAddLegacyStats(int lid, const base::ListValue& value);
   void SendOnSuspendOnUIThread();
 
   // mojom::PeerConnectionTrackerHost implementation.
+  void AddPeerConnection(mojom::PeerConnectionInfoPtr info) override;
   void RemovePeerConnection(int lid) override;
   void UpdatePeerConnection(int lid,
                             const std::string& type,
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 2a13cec..2a936a1 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -172,7 +172,17 @@
                                 const base::Time end,
                                 base::OnceClosure callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  GetShaderCacheFactorySingleton()->ClearByPath(
+  gpu::ShaderCacheFactory* shader_cache_factory =
+      GetShaderCacheFactorySingleton();
+
+  // May be null in tests where it is difficult to plumb through a test storage
+  // partition.
+  if (!shader_cache_factory) {
+    std::move(callback).Run();
+    return;
+  }
+
+  shader_cache_factory->ClearByPath(
       path, begin, end,
       base::BindOnce(&ClearedShaderCache, std::move(callback)));
 }
diff --git a/content/browser/web_package/bundled_exchanges_browsertest.cc b/content/browser/web_package/bundled_exchanges_browsertest.cc
index c1ca01d..aff6c09f 100644
--- a/content/browser/web_package/bundled_exchanges_browsertest.cc
+++ b/content/browser/web_package/bundled_exchanges_browsertest.cc
@@ -11,9 +11,12 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -27,14 +30,36 @@
 namespace content {
 namespace {
 
-base::FilePath GetDefaultTestDataPath() {
+// "%2F" is treated as an invalid character for file URLs.
+static constexpr char kInvalidFileUrl[] = "file:///tmp/test%2F/a.wbn";
+
+base::FilePath GetTestDataPath(base::StringPiece file) {
   base::FilePath test_data_dir;
   CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
-  return test_data_dir.Append(
-      base::FilePath(FILE_PATH_LITERAL("content/test/data/bundled_exchanges/"
-                                       "bundled_exchanges_browsertest.wbn")));
+  return test_data_dir
+      .Append(base::FilePath(
+          FILE_PATH_LITERAL("content/test/data/bundled_exchanges")))
+      .AppendASCII(file);
 }
 
+#if defined(OS_ANDROID)
+GURL CopyFileAndGetContentUri(const base::FilePath& file) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::FilePath tmp_dir;
+  CHECK(base::GetTempDir(&tmp_dir));
+  // The directory name "bundled_exchanges" must be kept in sync with
+  // content/shell/android/browsertests_apk/res/xml/file_paths.xml
+  base::FilePath tmp_wbn_dir = tmp_dir.AppendASCII("bundled_exchanges");
+  CHECK(base::CreateDirectoryAndGetError(tmp_wbn_dir, nullptr));
+  base::FilePath tmp_dir_in_tmp_wbn_dir;
+  CHECK(
+      base::CreateTemporaryDirInDir(tmp_wbn_dir, "", &tmp_dir_in_tmp_wbn_dir));
+  base::FilePath temp_file = tmp_dir_in_tmp_wbn_dir.Append(file.BaseName());
+  CHECK(base::CopyFile(file, temp_file));
+  return GURL(base::GetContentUriFromFilePath(temp_file).value());
+}
+#endif  // OS_ANDROID
+
 class TestBrowserClient : public ContentBrowserClient {
  public:
   TestBrowserClient() = default;
@@ -65,8 +90,68 @@
   DISALLOW_COPY_AND_ASSIGN(FinishNavigationObserver);
 };
 
+ContentBrowserClient* MaybeSetBrowserClientForTesting(
+    ContentBrowserClient* browser_client) {
+#if defined(OS_ANDROID)
+  // TODO(crbug.com/864403): It seems that we call unsupported Android APIs on
+  // KitKat when we set a ContentBrowserClient. Don't call such APIs and make
+  // this test available on KitKat.
+  int32_t major_version = 0, minor_version = 0, bugfix_version = 0;
+  base::SysInfo::OperatingSystemVersionNumbers(&major_version, &minor_version,
+                                               &bugfix_version);
+  if (major_version < 5)
+    return nullptr;
+#endif  // defined(OS_ANDROID)
+  return SetBrowserClientForTesting(browser_client);
+}
+
 }  // namespace
 
+class InvalidTrustableBundledExchangesFileUrlBrowserTest
+    : public ContentBrowserTest {
+ protected:
+  InvalidTrustableBundledExchangesFileUrlBrowserTest() = default;
+  ~InvalidTrustableBundledExchangesFileUrlBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    ContentBrowserTest::SetUpOnMainThread();
+    original_client_ = MaybeSetBrowserClientForTesting(&browser_client_);
+  }
+
+  void TearDownOnMainThread() override {
+    ContentBrowserTest::TearDownOnMainThread();
+    if (original_client_)
+      SetBrowserClientForTesting(original_client_);
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitchASCII(switches::kTrustableBundledExchangesFileUrl,
+                                    kInvalidFileUrl);
+  }
+
+  ContentBrowserClient* original_client_ = nullptr;
+
+ private:
+  TestBrowserClient browser_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(InvalidTrustableBundledExchangesFileUrlBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(InvalidTrustableBundledExchangesFileUrlBrowserTest,
+                       NoCrashOnNavigation) {
+  // Don't run the test if we couldn't override BrowserClient. It happens only
+  // on Android Kitkat or older systems.
+  if (!original_client_)
+    return;
+  base::RunLoop run_loop;
+  FinishNavigationObserver finish_navigation_observer(shell()->web_contents(),
+                                                      run_loop.QuitClosure());
+  EXPECT_FALSE(NavigateToURL(shell()->web_contents(), GURL(kInvalidFileUrl)));
+  run_loop.Run();
+  ASSERT_TRUE(finish_navigation_observer.error_code());
+  EXPECT_EQ(net::ERR_INVALID_URL, *finish_navigation_observer.error_code());
+}
+
 class BundledExchangesTrustableFileBrowserTestBase : public ContentBrowserTest {
  protected:
   BundledExchangesTrustableFileBrowserTestBase() = default;
@@ -78,17 +163,7 @@
 
   void SetUpOnMainThread() override {
     ContentBrowserTest::SetUpOnMainThread();
-#if defined(OS_ANDROID)
-    // TODO(crbug.com/864403): It seems that we call unsupported Android APIs on
-    // KitKat when we set a ContentBrowserClient. Don't call such APIs and make
-    // this test available on KitKat.
-    int32_t major_version = 0, minor_version = 0, bugfix_version = 0;
-    base::SysInfo::OperatingSystemVersionNumbers(&major_version, &minor_version,
-                                                 &bugfix_version);
-    if (major_version < 5)
-      return;
-#endif
-    original_client_ = SetBrowserClientForTesting(&browser_client_);
+    original_client_ = MaybeSetBrowserClientForTesting(&browser_client_);
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -144,21 +219,14 @@
  protected:
   BundledExchangesTrustableFileBrowserTest() {
     if (GetParam() == TestFilePathMode::kNormalFilePath) {
-      test_data_url_ = net::FilePathToFileURL(GetDefaultTestDataPath());
+      test_data_url_ = net::FilePathToFileURL(
+          GetTestDataPath("bundled_exchanges_browsertest.wbn"));
       return;
     }
 #if defined(OS_ANDROID)
     DCHECK_EQ(TestFilePathMode::kContentURI, GetParam());
-    base::FilePath tmp_dir;
-    CHECK(base::GetTempDir(&tmp_dir));
-    // The directory name "bundled_exchanges" must be kept in sync with
-    // content/shell/android/browsertests_apk/res/xml/file_paths.xml
-    base::FilePath tmp_wbn_dir = tmp_dir.AppendASCII("bundled_exchanges");
-    CHECK(base::CreateDirectoryAndGetError(tmp_wbn_dir, nullptr));
-    base::FilePath temp_file;
-    CHECK(base::CreateTemporaryFileInDir(tmp_wbn_dir, &temp_file));
-    CHECK(base::CopyFile(GetDefaultTestDataPath(), temp_file));
-    test_data_url_ = GURL(base::GetContentUriFromFilePath(temp_file).value());
+    test_data_url_ = CopyFileAndGetContentUri(
+        GetTestDataPath("bundled_exchanges_browsertest.wbn"));
 #endif  // OS_ANDROID
   }
   ~BundledExchangesTrustableFileBrowserTest() override = default;
@@ -225,4 +293,69 @@
             *finish_navigation_observer.error_code());
 }
 
+class BundledExchangesFileBrowserTest
+    : public testing::WithParamInterface<TestFilePathMode>,
+      public ContentBrowserTest {
+ protected:
+  BundledExchangesFileBrowserTest() = default;
+  ~BundledExchangesFileBrowserTest() override = default;
+
+  void SetUp() override {
+    feature_list_.InitWithFeatures({features::kBundledHTTPExchanges}, {});
+    ContentBrowserTest::SetUp();
+  }
+
+  GURL GetTestUrlForFile(base::FilePath file_path) const {
+    switch (GetParam()) {
+      case TestFilePathMode::kNormalFilePath:
+        return net::FilePathToFileURL(file_path);
+#if defined(OS_ANDROID)
+      case TestFilePathMode::kContentURI:
+        return CopyFileAndGetContentUri(file_path);
+#endif  // OS_ANDROID
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(BundledExchangesFileBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_P(BundledExchangesFileBrowserTest, BasicNavigation) {
+  const GURL test_data_url =
+      GetTestUrlForFile(GetTestDataPath("bundled_exchanges_browsertest.wbn"));
+  base::string16 expected_title = base::ASCIIToUTF16("Ready");
+  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
+  EXPECT_TRUE(
+      NavigateToURL(shell()->web_contents(), test_data_url,
+                    GURL(test_data_url.spec() + "?https://test.example.org/")));
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+}
+
+IN_PROC_BROWSER_TEST_P(BundledExchangesFileBrowserTest,
+                       InvalidBundledExchangeFile) {
+  const GURL test_data_url =
+      GetTestUrlForFile(GetTestDataPath("invalid_bundled_exchanges.wbn"));
+
+  base::RunLoop run_loop;
+  FinishNavigationObserver finish_navigation_observer(shell()->web_contents(),
+                                                      run_loop.QuitClosure());
+  EXPECT_FALSE(NavigateToURL(shell()->web_contents(), test_data_url,
+                             GURL("https://test.example.org/")));
+  run_loop.Run();
+  ASSERT_TRUE(finish_navigation_observer.error_code());
+  EXPECT_EQ(net::ERR_INVALID_BUNDLED_EXCHANGES,
+            *finish_navigation_observer.error_code());
+}
+
+INSTANTIATE_TEST_SUITE_P(BundledExchangesFileBrowserTest,
+                         BundledExchangesFileBrowserTest,
+                         testing::Values(TestFilePathMode::kNormalFilePath
+#if defined(OS_ANDROID)
+                                         ,
+                                         TestFilePathMode::kContentURI
+#endif  // OS_ANDROID
+                                         ));
+
 }  // namespace content
diff --git a/content/browser/web_package/bundled_exchanges_handle.cc b/content/browser/web_package/bundled_exchanges_handle.cc
index 0dd7b0a..5fbd721 100644
--- a/content/browser/web_package/bundled_exchanges_handle.cc
+++ b/content/browser/web_package/bundled_exchanges_handle.cc
@@ -5,12 +5,15 @@
 #include "content/browser/web_package/bundled_exchanges_handle.h"
 
 #include "base/bind.h"
+#include "base/memory/ptr_util.h"
 #include "base/sequence_checker.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
 #include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/browser/web_package/bundled_exchanges_reader.h"
+#include "content/browser/web_package/bundled_exchanges_source.h"
 #include "content/browser/web_package/bundled_exchanges_url_loader_factory.h"
+#include "content/browser/web_package/bundled_exchanges_utils.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -20,6 +23,7 @@
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "net/http/http_util.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/data_decoder/public/mojom/bundled_exchanges_parser.mojom.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 
 namespace content {
@@ -104,17 +108,161 @@
 };
 
 // A class to inherit NavigationLoaderInterceptor for the navigation to a
-// BundledExchanges.
-class Interceptor final : public NavigationLoaderInterceptor {
+// untrustable BundledExchanges file (eg: "file:///tmp/a.wbn").
+// The overridden methods of NavigationLoaderInterceptor are called in the
+// following sequence:
+// [1] MaybeCreateLoader() is called for all navigation requests. It calls the
+//     |callback| with the a null RequestHandler.
+// [2] MaybeCreateLoaderForResponse() is called for all navigation responses.
+//     If the response mime type is not "application/webbundle", returns false.
+//     Otherwise starts reading the metadata and returns true. Once the metadata
+//     is read, OnMetadataReady() is called, and a redirect loader is
+//     created to redirect the navigation request to the Bundle's synthesized
+//     primary URL ("file:///tmp/a.wbn?https://example.com/a.html").
+// [3] MaybeCreateLoader() is called again for the redirect. It continues on
+//     StartResponse() to create the loader for the main resource.
+class InterceptorForFile final : public NavigationLoaderInterceptor {
  public:
-  using RepeatingRequestHandler = base::RepeatingCallback<void(
-      const network::ResourceRequest& resource_request,
-      network::mojom::URLLoaderRequest,
-      network::mojom::URLLoaderClientPtr)>;
+  using DoneCallback = base::OnceCallback<void(
+      const GURL& base_url_override,
+      std::unique_ptr<BundledExchangesURLLoaderFactory> url_loader_factory)>;
 
-  explicit Interceptor(RepeatingRequestHandler request_handler)
-      : request_handler_(request_handler) {}
-  ~Interceptor() override {
+  explicit InterceptorForFile(DoneCallback done_callback)
+      : done_callback_(std::move(done_callback)) {}
+  ~InterceptorForFile() override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  }
+
+ private:
+  // NavigationLoaderInterceptor implementation
+  void MaybeCreateLoader(
+      const network::ResourceRequest& tentative_resource_request,
+      BrowserContext* browser_context,
+      LoaderCallback callback,
+      FallbackCallback fallback_callback) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    // InterceptorForFile::MaybeCreateLoader() creates a loader only after
+    // recognising that the response is a bundled exchange file at
+    // MaybeCreateLoaderForResponse() and successfully created
+    // |url_loader_factory_|.
+    if (!url_loader_factory_) {
+      std::move(callback).Run({});
+      return;
+    }
+    std::move(callback).Run(base::BindOnce(&InterceptorForFile::StartResponse,
+                                           weak_factory_.GetWeakPtr()));
+  }
+
+  bool MaybeCreateLoaderForResponse(
+      const network::ResourceRequest& request,
+      const network::ResourceResponseHead& response_head,
+      mojo::ScopedDataPipeConsumerHandle* response_body,
+      network::mojom::URLLoaderPtr* loader,
+      network::mojom::URLLoaderClientRequest* client_request,
+      ThrottlingURLLoader* url_loader,
+      bool* skip_other_interceptors,
+      bool* will_return_unsafe_redirect) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK(bundled_exchanges_utils::IsSupprtedFileScheme(request.url));
+    if (response_head.mime_type !=
+        bundled_exchanges_utils::
+            kBundledExchangesFileMimeTypeWithoutParameters) {
+      return false;
+    }
+    std::unique_ptr<BundledExchangesSource> source =
+        BundledExchangesSource::MaybeCreateFromFileUrl(request.url);
+    if (!source)
+      return false;
+    reader_ = std::make_unique<BundledExchangesReader>(*source);
+    reader_->ReadMetadata(base::BindOnce(&InterceptorForFile::OnMetadataReady,
+                                         weak_factory_.GetWeakPtr(), request));
+    *client_request = forwarding_client_.BindNewPipeAndPassReceiver();
+    *will_return_unsafe_redirect = true;
+    return true;
+  }
+
+  void OnMetadataReady(
+      network::ResourceRequest request,
+      data_decoder::mojom::BundleMetadataParseErrorPtr metadata_error) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    if (metadata_error) {
+      forwarding_client_->OnComplete(network::URLLoaderCompletionStatus(
+          net::ERR_INVALID_BUNDLED_EXCHANGES));
+      forwarding_client_.reset();
+      return;
+    }
+    DCHECK(reader_);
+    primary_url_ = reader_->GetPrimaryURL();
+    url_loader_factory_ =
+        std::make_unique<BundledExchangesURLLoaderFactory>(std::move(reader_));
+
+    const GURL new_url =
+        bundled_exchanges_utils::GetSynthesizedUrlForBundledExchanges(
+            request.url, primary_url_);
+    auto redirect_loader =
+        std::make_unique<PrimaryURLRedirectLoader>(forwarding_client_.Unbind());
+    redirect_loader->OnReadyToRedirect(request, new_url);
+  }
+
+  void StartResponse(const network::ResourceRequest& resource_request,
+                     network::mojom::URLLoaderRequest request,
+                     network::mojom::URLLoaderClientPtr client) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    network::ResourceRequest new_resource_request = resource_request;
+    new_resource_request.url = primary_url_;
+    url_loader_factory_->CreateLoaderAndStart(
+        std::move(request), /*routing_id=*/0, /*request_id=*/0, /*options=*/0,
+        new_resource_request, std::move(client),
+        net::MutableNetworkTrafficAnnotationTag(kTrafficAnnotation));
+    std::move(done_callback_).Run(primary_url_, std::move(url_loader_factory_));
+  }
+
+  DoneCallback done_callback_;
+  std::unique_ptr<BundledExchangesReader> reader_;
+  GURL primary_url_;
+  std::unique_ptr<BundledExchangesURLLoaderFactory> url_loader_factory_;
+
+  mojo::Remote<network::mojom::URLLoaderClient> forwarding_client_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<InterceptorForFile> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(InterceptorForFile);
+};
+
+// A class to inherit NavigationLoaderInterceptor for the navigation to a
+// trustable BundledExchanges file (eg: "file:///tmp/a.wbn").
+// The overridden methods of NavigationLoaderInterceptor are called in the
+// following sequence:
+// [1] MaybeCreateLoader() is called for the navigation request to the trustable
+//     BundledExchanges file. It continues on CreateURLLoader() to create the
+//     loader for the main resource.
+//     - If OnMetadataReady() has not been called yet:
+//         Wait for OnMetadataReady() to be called.
+//     - If OnMetadataReady() was called with an error:
+//         Completes the request with ERR_INVALID_BUNDLED_EXCHANGES.
+//     - If OnMetadataReady() was called whthout errors:
+//         A redirect loader is created to redirect the navigation request to
+//         the Bundle's primary URL ("https://example.com/a.html").
+// [2] MaybeCreateLoader() is called again for the redirect. It continues on
+//     CreateURLLoader() to create the loader for the main resource.
+class InterceptorForTrustableFile final : public NavigationLoaderInterceptor {
+ public:
+  using DoneCallback = base::OnceCallback<void(
+      const GURL& base_url_override,
+      std::unique_ptr<BundledExchangesURLLoaderFactory> url_loader_factory)>;
+
+  InterceptorForTrustableFile(std::unique_ptr<BundledExchangesSource> source,
+                              DoneCallback done_callback)
+      : source_(std::move(source)),
+        reader_(std::make_unique<BundledExchangesReader>(*source_)),
+        done_callback_(std::move(done_callback)) {
+    reader_->ReadMetadata(
+        base::BindOnce(&InterceptorForTrustableFile::OnMetadataReady,
+                       weak_factory_.GetWeakPtr()));
+  }
+  ~InterceptorForTrustableFile() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   }
 
@@ -125,42 +273,125 @@
                          LoaderCallback callback,
                          FallbackCallback fallback_callback) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    std::move(callback).Run(request_handler_);
+    std::move(callback).Run(
+        base::BindOnce(&InterceptorForTrustableFile::CreateURLLoader,
+                       weak_factory_.GetWeakPtr()));
   }
 
-  RepeatingRequestHandler request_handler_;
+  void CreateURLLoader(const network::ResourceRequest& resource_request,
+                       network::mojom::URLLoaderRequest request,
+                       network::mojom::URLLoaderClientPtr client) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    if (metadata_error_) {
+      client->OnComplete(network::URLLoaderCompletionStatus(
+          net::ERR_INVALID_BUNDLED_EXCHANGES));
+      return;
+    }
+
+    if (!url_loader_factory_) {
+      // This must be the first request to the bundled exchange file.
+      DCHECK_EQ(source_->url(), resource_request.url);
+      pending_resource_request_ = resource_request;
+      pending_request_ = std::move(request);
+      pending_client_ = std::move(client);
+      return;
+    }
+
+    // Currently |source_| must be a local file. And the bundle's primary URL
+    // can't be a local file URL. So while handling redirected request to the
+    // primary URL, |resource_request.url| must not be same as the |source_|'s
+    // URL.
+    if (source_->url() != resource_request.url) {
+      url_loader_factory_->CreateLoaderAndStart(
+          std::move(request), /*routing_id=*/0, /*request_id=*/0, /*options=*/0,
+          resource_request, std::move(client),
+          net::MutableNetworkTrafficAnnotationTag(kTrafficAnnotation));
+      std::move(done_callback_)
+          .Run(
+              /*base_url_override=*/GURL(), std::move(url_loader_factory_));
+      return;
+    }
+
+    auto redirect_loader =
+        std::make_unique<PrimaryURLRedirectLoader>(client.PassInterface());
+    redirect_loader->OnReadyToRedirect(resource_request, primary_url_);
+    mojo::MakeSelfOwnedReceiver(
+        std::move(redirect_loader),
+        mojo::PendingReceiver<network::mojom::URLLoader>(std::move(request)));
+  }
+
+  void OnMetadataReady(data_decoder::mojom::BundleMetadataParseErrorPtr error) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK(!url_loader_factory_);
+
+    if (error) {
+      metadata_error_ = std::move(error);
+    } else {
+      primary_url_ = reader_->GetPrimaryURL();
+      url_loader_factory_ = std::make_unique<BundledExchangesURLLoaderFactory>(
+          std::move(reader_));
+    }
+
+    if (pending_request_) {
+      DCHECK(pending_client_);
+      CreateURLLoader(pending_resource_request_, std::move(pending_request_),
+                      std::move(pending_client_));
+    }
+  }
+
+  std::unique_ptr<BundledExchangesSource> source_;
+  std::unique_ptr<BundledExchangesReader> reader_;
+  DoneCallback done_callback_;
+
+  network::ResourceRequest pending_resource_request_;
+  network::mojom::URLLoaderRequest pending_request_;
+  network::mojom::URLLoaderClientPtr pending_client_;
+
+  std::unique_ptr<BundledExchangesURLLoaderFactory> url_loader_factory_;
+
+  GURL primary_url_;
+  data_decoder::mojom::BundleMetadataParseErrorPtr metadata_error_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  DISALLOW_COPY_AND_ASSIGN(Interceptor);
+  base::WeakPtrFactory<InterceptorForTrustableFile> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(InterceptorForTrustableFile);
 };
 
 }  // namespace
 
-BundledExchangesHandle::BundledExchangesHandle() {}
-
-BundledExchangesHandle::BundledExchangesHandle(
-    std::unique_ptr<BundledExchangesSource> bundled_exchanges_source)
-    : source_(std::move(bundled_exchanges_source)),
-      reader_(std::make_unique<BundledExchangesReader>(*source_)) {
-  DCHECK(source_->is_trusted());
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  reader_->ReadMetadata(base::BindOnce(&BundledExchangesHandle::OnMetadataReady,
-                                       weak_factory_.GetWeakPtr()));
+// static
+std::unique_ptr<BundledExchangesHandle>
+BundledExchangesHandle::CreateForFile() {
+  auto handle = base::WrapUnique(new BundledExchangesHandle());
+  handle->SetInterceptor(std::make_unique<InterceptorForFile>(
+      base::BindOnce(&BundledExchangesHandle::OnBundledExchangesFileLoaded,
+                     handle->weak_factory_.GetWeakPtr())));
+  return handle;
 }
 
-BundledExchangesHandle::~BundledExchangesHandle() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+// static
+std::unique_ptr<BundledExchangesHandle>
+BundledExchangesHandle::CreateForTrustableFile(
+    std::unique_ptr<BundledExchangesSource> source) {
+  DCHECK(source->is_trusted());
+  auto handle = base::WrapUnique(new BundledExchangesHandle());
+  handle->SetInterceptor(std::make_unique<InterceptorForTrustableFile>(
+      std::move(source),
+      base::BindOnce(&BundledExchangesHandle::OnBundledExchangesFileLoaded,
+                     handle->weak_factory_.GetWeakPtr())));
+  return handle;
 }
 
+BundledExchangesHandle::BundledExchangesHandle() = default;
+
+BundledExchangesHandle::~BundledExchangesHandle() = default;
+
 std::unique_ptr<NavigationLoaderInterceptor>
-BundledExchangesHandle::CreateInterceptor() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return std::make_unique<Interceptor>(
-      reader_ ? base::BindRepeating(&BundledExchangesHandle::CreateURLLoader,
-                                    weak_factory_.GetWeakPtr())
-              : base::NullCallback());
+BundledExchangesHandle::TakeInterceptor() {
+  DCHECK(interceptor_);
+  return std::move(interceptor_);
 }
 
 void BundledExchangesHandle::CreateURLLoaderFactory(
@@ -177,60 +408,16 @@
   return !!url_loader_factory_;
 }
 
-void BundledExchangesHandle::CreateURLLoader(
-    const network::ResourceRequest& resource_request,
-    network::mojom::URLLoaderRequest request,
-    network::mojom::URLLoaderClientPtr client) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (metadata_error_) {
-    client->OnComplete(
-        network::URLLoaderCompletionStatus(net::ERR_INVALID_BUNDLED_EXCHANGES));
-    return;
-  }
-
-  if (!url_loader_factory_) {
-    // This must be the first request to the bundled exchange file.
-    DCHECK_EQ(source_->url(), resource_request.url);
-    pending_create_url_loader_task_ = base::Bind(
-        &BundledExchangesHandle::CreateURLLoader, base::Unretained(this),
-        resource_request, base::Passed(&request), base::Passed(&client));
-    return;
-  }
-
-  // Currently |source_| must be a local file. And the bundle's primary URL
-  // can't be a local file URL. So while handling redirected request to the
-  // primary URL, |resource_request.url| must not be same as the |source|'s URL.
-  if (source_->url() != resource_request.url) {
-    url_loader_factory_->CreateLoaderAndStart(
-        std::move(request), /*routing_id=*/0, /*request_id=*/0, /*options=*/0,
-        resource_request, std::move(client),
-        net::MutableNetworkTrafficAnnotationTag(kTrafficAnnotation));
-    return;
-  }
-
-  auto redirect_loader =
-      std::make_unique<PrimaryURLRedirectLoader>(client.PassInterface());
-  redirect_loader->OnReadyToRedirect(resource_request, primary_url_);
-  mojo::MakeSelfOwnedReceiver(
-      std::move(redirect_loader),
-      mojo::PendingReceiver<network::mojom::URLLoader>(std::move(request)));
+void BundledExchangesHandle::SetInterceptor(
+    std::unique_ptr<NavigationLoaderInterceptor> interceptor) {
+  interceptor_ = std::move(interceptor);
 }
 
-void BundledExchangesHandle::OnMetadataReady(
-    data_decoder::mojom::BundleMetadataParseErrorPtr error) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(!url_loader_factory_);
-
-  if (error) {
-    metadata_error_ = std::move(error);
-  } else {
-    primary_url_ = reader_->GetPrimaryURL();
-    url_loader_factory_ =
-        std::make_unique<BundledExchangesURLLoaderFactory>(std::move(reader_));
-  }
-
-  if (pending_create_url_loader_task_)
-    std::move(pending_create_url_loader_task_).Run();
+void BundledExchangesHandle::OnBundledExchangesFileLoaded(
+    const GURL& base_url_override,
+    std::unique_ptr<BundledExchangesURLLoaderFactory> url_loader_factory) {
+  base_url_override_ = base_url_override;
+  url_loader_factory_ = std::move(url_loader_factory);
 }
 
 }  // namespace content
diff --git a/content/browser/web_package/bundled_exchanges_handle.h b/content/browser/web_package/bundled_exchanges_handle.h
index 8289740..efe932f 100644
--- a/content/browser/web_package/bundled_exchanges_handle.h
+++ b/content/browser/web_package/bundled_exchanges_handle.h
@@ -6,38 +6,34 @@
 #define CONTENT_BROWSER_WEB_PACKAGE_BUNDLED_EXCHANGES_HANDLE_H_
 
 #include <memory>
-#include <string>
 
-#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "content/browser/web_package/bundled_exchanges_source.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "services/data_decoder/public/mojom/bundled_exchanges_parser.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
 
-class BundledExchangesReader;
+class BundledExchangesSource;
 class BundledExchangesURLLoaderFactory;
 class NavigationLoaderInterceptor;
 
 // A class to provide interfaces to communicate with a BundledExchanges for
 // loading. Running on the UI thread.
-class BundledExchangesHandle final {
+class BundledExchangesHandle {
  public:
-  BundledExchangesHandle();
-  explicit BundledExchangesHandle(
-      std::unique_ptr<BundledExchangesSource> bundled_exchanges_source);
+  static std::unique_ptr<BundledExchangesHandle> CreateForFile();
+  static std::unique_ptr<BundledExchangesHandle> CreateForTrustableFile(
+      std::unique_ptr<BundledExchangesSource> source);
+
   ~BundledExchangesHandle();
 
-  // Creates a NavigationLoaderInterceptor instance to handle the request for
+  // Takes a NavigationLoaderInterceptor instance to handle the request for
   // a BundledExchanges, to redirect to the entry URL of the BundledExchanges,
   // and to load the main exchange from the BundledExchanges.
-  std::unique_ptr<NavigationLoaderInterceptor> CreateInterceptor();
+  std::unique_ptr<NavigationLoaderInterceptor> TakeInterceptor();
 
   // Creates a URLLoaderFactory to load resources from the BundledExchanges.
   void CreateURLLoaderFactory(
@@ -47,19 +43,24 @@
   // Checks if a valid BundledExchanges is attached, opened, and ready for use.
   bool IsReadyForLoading();
 
+  // The base URL which will be set for the document to support relative path
+  // subresource loading in unsigned bundled exchanges file.
+  const GURL& base_url_override() const { return base_url_override_; }
+
  private:
-  void CreateURLLoader(const network::ResourceRequest& resource_request,
-                       network::mojom::URLLoaderRequest request,
-                       network::mojom::URLLoaderClientPtr client);
-  void OnMetadataReady(data_decoder::mojom::BundleMetadataParseErrorPtr error);
+  BundledExchangesHandle();
 
-  base::OnceClosure pending_create_url_loader_task_;
+  void SetInterceptor(std::unique_ptr<NavigationLoaderInterceptor> interceptor);
 
-  std::unique_ptr<BundledExchangesSource> source_;
-  std::unique_ptr<BundledExchangesReader> reader_;
+  // Called when succeeded to load the bundled exchanges file.
+  void OnBundledExchangesFileLoaded(
+      const GURL& base_url_override,
+      std::unique_ptr<BundledExchangesURLLoaderFactory> url_loader_factory);
+
+  std::unique_ptr<NavigationLoaderInterceptor> interceptor_;
+
+  GURL base_url_override_;
   std::unique_ptr<BundledExchangesURLLoaderFactory> url_loader_factory_;
-  GURL primary_url_;
-  data_decoder::mojom::BundleMetadataParseErrorPtr metadata_error_;
 
   base::WeakPtrFactory<BundledExchangesHandle> weak_factory_{this};
 
diff --git a/content/browser/web_package/bundled_exchanges_source.cc b/content/browser/web_package/bundled_exchanges_source.cc
index 068bf686..c978f84 100644
--- a/content/browser/web_package/bundled_exchanges_source.cc
+++ b/content/browser/web_package/bundled_exchanges_source.cc
@@ -18,18 +18,39 @@
 
 // static
 std::unique_ptr<BundledExchangesSource>
-BundledExchangesSource::CreateFromTrustedFileUrl(const GURL& url) {
+BundledExchangesSource::MaybeCreateFromTrustedFileUrl(const GURL& url) {
 #if defined(OS_ANDROID)
   if (url.SchemeIs(url::kContentScheme)) {
     const base::FilePath file_path = base::FilePath(url.spec());
-    return base::WrapUnique(new BundledExchangesSource(true, file_path, url));
+    return base::WrapUnique(
+        new BundledExchangesSource(/*is_trusted=*/true, file_path, url));
   }
 #endif
   DCHECK(url.SchemeIsFile());
   base::FilePath file_path;
   if (!net::FileURLToFilePath(url, &file_path))
     return nullptr;
-  return base::WrapUnique(new BundledExchangesSource(true, file_path, url));
+  return base::WrapUnique(
+      new BundledExchangesSource(/*is_trusted=*/true, file_path, url));
+}
+
+// static
+std::unique_ptr<BundledExchangesSource>
+BundledExchangesSource::MaybeCreateFromFileUrl(const GURL& url) {
+  base::FilePath file_path;
+  if (url.SchemeIsFile()) {
+    if (net::FileURLToFilePath(url, &file_path)) {
+      return base::WrapUnique(
+          new BundledExchangesSource(/*is_trusted=*/false, file_path, url));
+    }
+  }
+#if defined(OS_ANDROID)
+  if (url.SchemeIs(url::kContentScheme)) {
+    return base::WrapUnique(new BundledExchangesSource(
+        /*is_trusted=*/false, base::FilePath(url.spec()), url));
+  }
+#endif
+  return nullptr;
 }
 
 std::unique_ptr<BundledExchangesSource> BundledExchangesSource::Clone() const {
diff --git a/content/browser/web_package/bundled_exchanges_source.h b/content/browser/web_package/bundled_exchanges_source.h
index 2b2985a4..1ad7ee4 100644
--- a/content/browser/web_package/bundled_exchanges_source.h
+++ b/content/browser/web_package/bundled_exchanges_source.h
@@ -21,7 +21,14 @@
 // A class to abstract required information to access a BundledExchanges.
 class CONTENT_EXPORT BundledExchangesSource {
  public:
-  static std::unique_ptr<BundledExchangesSource> CreateFromTrustedFileUrl(
+  // Used only for testing navigation to a trustable BundledExchanges source
+  // with --trustable-bundled-exchanges-file-url flag. Returns null when failed
+  // to get the filename from the |url|.
+  static std::unique_ptr<BundledExchangesSource> MaybeCreateFromTrustedFileUrl(
+      const GURL& url);
+  // Returns a new BundledExchangesSource for the |url| if the scheme of |url|
+  // is file: (or content: on Android). Otherwise returns null.
+  static std::unique_ptr<BundledExchangesSource> MaybeCreateFromFileUrl(
       const GURL& url);
 
   ~BundledExchangesSource() = default;
@@ -34,10 +41,6 @@
   // the BundledExchanges as the origin for the content. Otherwise, we will use
   // the origin that serves the BundledExchanges itself. For instance, if the
   // BundledExchanges is in a local file system, file:// should be the origin.
-  //
-  // Currently this flag is always true because we only support
-  // trustable-bundled-exchanges-file loading. TODO(horo): Implement
-  // untrusted bundled exchanges file loading.
   bool is_trusted() const { return is_trusted_; }
 
   const base::FilePath& file_path() const { return file_path_; }
diff --git a/content/browser/web_package/bundled_exchanges_utils.cc b/content/browser/web_package/bundled_exchanges_utils.cc
new file mode 100644
index 0000000..af0fcd56
--- /dev/null
+++ b/content/browser/web_package/bundled_exchanges_utils.cc
@@ -0,0 +1,101 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/web_package/bundled_exchanges_utils.h"
+
+#include "base/command_line.h"
+#include "base/feature_list.h"
+#include "build/build_config.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
+#include "url/gurl.h"
+
+#if defined(OS_ANDROID)
+#include "url/url_constants.h"
+#endif  // defined(OS_ANDROID)
+
+namespace content {
+namespace bundled_exchanges_utils {
+namespace {
+
+const base::FilePath::CharType kBundledExchangesFileExtension[] =
+    FILE_PATH_LITERAL(".wbn");
+
+}  // namespace
+
+bool IsSupprtedFileScheme(const GURL& url) {
+  if (url.SchemeIsFile())
+    return true;
+#if defined(OS_ANDROID)
+  if (url.SchemeIs(url::kContentScheme))
+    return true;
+#endif  // defined(OS_ANDROID)
+  return false;
+}
+
+bool CanLoadAsTrustableBundledExchangesFile(const GURL& url) {
+  if (!GetContentClient()->browser()->CanAcceptUntrustedExchangesIfNeeded())
+    return false;
+  if (!IsSupprtedFileScheme(url))
+    return false;
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTrustableBundledExchangesFileUrl)) {
+    return false;
+  }
+  return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+             switches::kTrustableBundledExchangesFileUrl) == url.spec();
+}
+
+bool CanLoadAsBundledExchangesFile(const GURL& url) {
+  if (!base::FeatureList::IsEnabled(features::kBundledHTTPExchanges))
+    return false;
+  return IsSupprtedFileScheme(url);
+}
+
+bool CanLoadAsBundledExchanges(const GURL& url, const std::string& mime_type) {
+  if (!base::FeatureList::IsEnabled(features::kBundledHTTPExchanges))
+    return false;
+  // Currently loading bundled exchanges file from server response is not
+  // implemented yet.
+  if (!IsSupprtedFileScheme(url))
+    return false;
+  return mime_type == kBundledExchangesFileMimeTypeWithoutParameters;
+}
+
+bool GetBundledExchangesFileMimeTypeFromFile(const base::FilePath& path,
+                                             std::string* mime_type) {
+  DCHECK(mime_type);
+  if (!base::FeatureList::IsEnabled(features::kBundledHTTPExchanges))
+    return false;
+  if (path.Extension() != kBundledExchangesFileExtension)
+    return false;
+  *mime_type = kBundledExchangesFileMimeTypeWithoutParameters;
+  return true;
+}
+
+GURL GetSynthesizedUrlForBundledExchanges(
+    const GURL& bundled_exchanges_file_url,
+    const GURL& url_in_bundles) {
+  url::Replacements<char> replacements;
+
+  url::Replacements<char> clear_ref;
+  clear_ref.ClearRef();
+  std::string query_string = url_in_bundles.ReplaceComponents(clear_ref).spec();
+  url::Component new_query(0, query_string.size());
+  replacements.SetQuery(query_string.c_str(), new_query);
+
+  if (!url_in_bundles.has_ref()) {
+    replacements.ClearRef();
+    return bundled_exchanges_file_url.ReplaceComponents(replacements);
+  }
+  url::Component new_ref(0, url_in_bundles.ref().size());
+  std::string ref_string = url_in_bundles.ref();
+  replacements.SetRef(ref_string.c_str(), new_ref);
+  return bundled_exchanges_file_url.ReplaceComponents(replacements);
+}
+
+}  // namespace bundled_exchanges_utils
+}  // namespace content
diff --git a/content/browser/web_package/bundled_exchanges_utils.h b/content/browser/web_package/bundled_exchanges_utils.h
new file mode 100644
index 0000000..590dfb3b
--- /dev/null
+++ b/content/browser/web_package/bundled_exchanges_utils.h
@@ -0,0 +1,72 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEB_PACKAGE_BUNDLED_EXCHANGES_UTILS_H_
+#define CONTENT_BROWSER_WEB_PACKAGE_BUNDLED_EXCHANGES_UTILS_H_
+
+#include "base/files/file_path.h"
+#include "content/common/content_export.h"
+
+class GURL;
+
+namespace content {
+namespace bundled_exchanges_utils {
+
+// The "application/webbundle" MIME type must have a "v" parameter whose value
+// is a format version [1]. But we use the "application/webbundle" MIME type
+// without "v" parameter while loading bundled exchanges files in local storage.
+// This is because Android's intent-filter is not designed to support such
+// parameters.
+// IntentResolver.queryIntent() [2] is called when a filer application calls
+// PackageManager.queryIntentActivities() to open files. But this queryIntent()
+// doesn't support Media Type's parameters matching. For example, if we add a
+// <intent-filter> with <data android:mimeType="application/webbundle;v=b1" />
+// in AndroidManifest.xml, the data with "application/webbundle; v=b1" which has
+// a space after ";" doesn't match the filter.
+// [1]
+// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#internet-media-type-registration
+// [2]
+// https://android.googlesource.com/platform/frameworks/base/+/4ab9511/services/core/java/com/android/server/IntentResolver.java#398
+constexpr char kBundledExchangesFileMimeTypeWithoutParameters[] =
+    "application/webbundle";
+
+// On Android, returns true if the url scheme is file or content. On other
+// platforms, returns true if the url scheme is file.
+bool IsSupprtedFileScheme(const GURL& url);
+
+// Returns true if |url| is the file URL which is specified with
+// --trustable-bundled-exchanges-file-url flag. Always returns false when
+// ContentBrowserClient::CanAcceptUntrustedExchangesIfNeeded() is false.
+bool CanLoadAsTrustableBundledExchangesFile(const GURL& url);
+
+// Returns whether bundled exchanges file can be loaded from the |url|. Always
+// returns false when BundledHTTPExchanges feature is not enabled.
+bool CanLoadAsBundledExchangesFile(const GURL& url);
+
+// Returns whether bundled exchanges file can be loaded from the |url| with
+// the |mime_type|. Always returns false when BundledHTTPExchanges feature is
+// not enabled.
+bool CanLoadAsBundledExchanges(const GURL& url, const std::string& mime_type);
+
+// Sets |mime_type| to "application/webbundle" and returns true, when
+// BundledHTTPExchanges feature is enabled, and the extension of the |path| is
+// ".wbn". Otherwise returns false.
+bool GetBundledExchangesFileMimeTypeFromFile(const base::FilePath& path,
+                                             std::string* mime_type);
+
+// Generate a synthesized URL which can indicate the url in bundled exchanges
+// file.
+// Example:
+//   bundled_exchanges_file_url: file:///dir/x.wbn?query1#ref1
+//   url_in_bundles: https://example.com/a.html?query2#ref2
+//      => synthesized URL:
+//           file:///dir/x.wbn?https://example.com/a.html?query2#ref2
+CONTENT_EXPORT GURL
+GetSynthesizedUrlForBundledExchanges(const GURL& bundled_exchanges_file_url,
+                                     const GURL& url_in_bundles);
+
+}  // namespace bundled_exchanges_utils
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_WEB_PACKAGE_BUNDLED_EXCHANGES_UTILS_H_
diff --git a/content/browser/web_package/bundled_exchanges_utils_unittest.cc b/content/browser/web_package/bundled_exchanges_utils_unittest.cc
new file mode 100644
index 0000000..44b0495
--- /dev/null
+++ b/content/browser/web_package/bundled_exchanges_utils_unittest.cc
@@ -0,0 +1,45 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/web_package/bundled_exchanges_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace content {
+namespace bundled_exchanges_utils {
+
+TEST(BundledExchangesUtilsTest, GetSynthesizedUrlForBundledExchanges) {
+  EXPECT_EQ(GURL("file:///dir/x.wbn?https://example.com/a.html"),
+            GetSynthesizedUrlForBundledExchanges(
+                GURL("file:///dir/x.wbn"), GURL("https://example.com/a.html")));
+
+  EXPECT_EQ(
+      GURL("file:///dir/x.wbn?https://example.com/a.html?query"),
+      GetSynthesizedUrlForBundledExchanges(
+          GURL("file:///dir/x.wbn"), GURL("https://example.com/a.html?query")));
+
+  EXPECT_EQ(GURL("file:///dir/x.wbn?https://example.com/a.html?query2"),
+            GetSynthesizedUrlForBundledExchanges(
+                GURL("file:///dir/x.wbn?query1"),
+                GURL("https://example.com/a.html?query2")));
+
+  EXPECT_EQ(
+      GURL("file:///dir/x.wbn?https://example.com/a.html"),
+      GetSynthesizedUrlForBundledExchanges(GURL("file:///dir/x.wbn#ref"),
+                                           GURL("https://example.com/a.html")));
+
+  EXPECT_EQ(GURL("file:///dir/x.wbn?https://example.com/a.html#ref2"),
+            GetSynthesizedUrlForBundledExchanges(
+                GURL("file:///dir/x.wbn#ref1"),
+                GURL("https://example.com/a.html#ref2")));
+
+  EXPECT_EQ(GURL("file:///dir/x.wbn?https://example.com/a.html?query2#ref2"),
+            GetSynthesizedUrlForBundledExchanges(
+                GURL("file:///dir/x.wbn?query1#ref1"),
+                GURL("https://example.com/a.html?query2#ref2")));
+}
+
+}  // namespace bundled_exchanges_utils
+}  // namespace content
diff --git a/content/browser/web_package/mock_bundled_exchanges_reader_factory.cc b/content/browser/web_package/mock_bundled_exchanges_reader_factory.cc
index b95ddb2..01f5c03 100644
--- a/content/browser/web_package/mock_bundled_exchanges_reader_factory.cc
+++ b/content/browser/web_package/mock_bundled_exchanges_reader_factory.cc
@@ -159,7 +159,7 @@
       return nullptr;
     }
 
-    auto source = BundledExchangesSource::CreateFromTrustedFileUrl(
+    auto source = BundledExchangesSource::MaybeCreateFromTrustedFileUrl(
         net::FilePathToFileURL(temp_file_path_));
     auto reader = std::make_unique<BundledExchangesReader>(*source);
 
diff --git a/content/browser/web_package/signed_exchange_request_handler.cc b/content/browser/web_package/signed_exchange_request_handler.cc
index 11a52c20..92d6dc3 100644
--- a/content/browser/web_package/signed_exchange_request_handler.cc
+++ b/content/browser/web_package/signed_exchange_request_handler.cc
@@ -81,7 +81,8 @@
     network::mojom::URLLoaderPtr* loader,
     network::mojom::URLLoaderClientRequest* client_request,
     ThrottlingURLLoader* url_loader,
-    bool* skip_other_interceptors) {
+    bool* skip_other_interceptors,
+    bool* will_return_unsafe_redirect) {
   DCHECK(!signed_exchange_loader_);
   if (!signed_exchange_utils::ShouldHandleAsSignedHTTPExchange(request.url,
                                                                response_head)) {
diff --git a/content/browser/web_package/signed_exchange_request_handler.h b/content/browser/web_package/signed_exchange_request_handler.h
index 079714a..09731c3d 100644
--- a/content/browser/web_package/signed_exchange_request_handler.h
+++ b/content/browser/web_package/signed_exchange_request_handler.h
@@ -57,7 +57,8 @@
       network::mojom::URLLoaderPtr* loader,
       network::mojom::URLLoaderClientRequest* client_request,
       ThrottlingURLLoader* url_loader,
-      bool* skip_other_interceptors) override;
+      bool* skip_other_interceptors,
+      bool* will_return_unsafe_redirect) override;
 
  private:
   void StartResponse(const network::ResourceRequest& resource_request,
diff --git a/content/browser/worker_host/shared_worker_service_impl.cc b/content/browser/worker_host/shared_worker_service_impl.cc
index f21a68f..810415e 100644
--- a/content/browser/worker_host/shared_worker_service_impl.cc
+++ b/content/browser/worker_host/shared_worker_service_impl.cc
@@ -132,9 +132,7 @@
   RenderFrameHost* main_frame =
       render_frame_host->frame_tree_node()->frame_tree()->GetMainFrame();
   if (!GetContentClient()->browser()->AllowSharedWorker(
-          info->url,
-          // TODO(crbug.com/989926): Get an actual site_for_cookies.
-          main_frame->GetLastCommittedURL(),
+          info->url, render_frame_host->ComputeSiteForCookies(),
           main_frame->GetLastCommittedOrigin(), info->name, constructor_origin,
           WebContentsImpl::FromRenderFrameHostID(client_process_id, frame_id)
               ->GetBrowserContext(),
diff --git a/content/browser/worker_host/worker_script_loader.cc b/content/browser/worker_host/worker_script_loader.cc
index 604c7a8..db2395f 100644
--- a/content/browser/worker_host/worker_script_loader.cc
+++ b/content/browser/worker_host/worker_script_loader.cc
@@ -295,13 +295,15 @@
   DCHECK(default_loader_used_);
   for (auto& interceptor : interceptors_) {
     bool skip_other_interceptors = false;
+    bool will_return_unsafe_redirect = false;
     if (interceptor->MaybeCreateLoaderForResponse(
             resource_request_, response_head, response_body,
             response_url_loader, response_client_request, url_loader,
-            &skip_other_interceptors)) {
+            &skip_other_interceptors, &will_return_unsafe_redirect)) {
       // Both ServiceWorkerRequestHandler and AppCacheRequestHandler don't set
-      // skip_other_interceptors.
+      // skip_other_interceptors nor will_return_unsafe_redirect.
       DCHECK(!skip_other_interceptors);
+      DCHECK(!will_return_unsafe_redirect);
       subresource_loader_params_ =
           interceptor->MaybeCreateSubresourceLoaderParams();
       return true;
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 88ec291..70aaa76 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -94,78 +94,12 @@
       return IDS_AX_DAY_OF_MONTH_FIELD_TEXT;
     case WebLocalizedString::kAXHourFieldText:
       return IDS_AX_HOUR_FIELD_TEXT;
-    case WebLocalizedString::kAXMediaDefault:
-      return IDS_AX_MEDIA_DEFAULT;
-    case WebLocalizedString::kAXMediaAudioElement:
-      return IDS_AX_MEDIA_AUDIO_ELEMENT;
-    case WebLocalizedString::kAXMediaVideoElement:
-      return IDS_AX_MEDIA_VIDEO_ELEMENT;
-    case WebLocalizedString::kAXMediaMuteButton:
-      return IDS_AX_MEDIA_MUTE_BUTTON;
-    case WebLocalizedString::kAXMediaUnMuteButton:
-      return IDS_AX_MEDIA_UNMUTE_BUTTON;
-    case WebLocalizedString::kAXMediaPlayButton:
-      return IDS_AX_MEDIA_PLAY_BUTTON;
-    case WebLocalizedString::kAXMediaPauseButton:
-      return IDS_AX_MEDIA_PAUSE_BUTTON;
-    case WebLocalizedString::kAXMediaCurrentTimeDisplay:
-      return IDS_AX_MEDIA_CURRENT_TIME_DISPLAY;
-    case WebLocalizedString::kAXMediaTimeRemainingDisplay:
-      return IDS_AX_MEDIA_TIME_REMAINING_DISPLAY;
-    case WebLocalizedString::kAXMediaEnterFullscreenButton:
-      return IDS_AX_MEDIA_ENTER_FULL_SCREEN_BUTTON;
-    case WebLocalizedString::kAXMediaExitFullscreenButton:
-      return IDS_AX_MEDIA_EXIT_FULL_SCREEN_BUTTON;
-    case WebLocalizedString::kAXMediaDisplayCutoutFullscreenButton:
-      return IDS_AX_MEDIA_DISPLAY_CUT_OUT_FULL_SCREEN_BUTTON;
-    case WebLocalizedString::kAXMediaEnterPictureInPictureButton:
-      return IDS_AX_MEDIA_ENTER_PICTURE_IN_PICTURE_BUTTON;
-    case WebLocalizedString::kAXMediaExitPictureInPictureButton:
-      return IDS_AX_MEDIA_EXIT_PICTURE_IN_PICTURE_BUTTON;
-    case WebLocalizedString::kAXMediaShowClosedCaptionsMenuButton:
-      return IDS_AX_MEDIA_SHOW_CLOSED_CAPTIONS_MENU_BUTTON;
-    case WebLocalizedString::kAXMediaHideClosedCaptionsMenuButton:
-      return IDS_AX_MEDIA_HIDE_CLOSED_CAPTIONS_MENU_BUTTON;
-    case WebLocalizedString::kAXMediaLoadingPanel:
-      return IDS_AX_MEDIA_LOADING_PANEL;
-    case WebLocalizedString::kAXMediaCastOffButton:
-      return IDS_AX_MEDIA_CAST_OFF_BUTTON;
-    case WebLocalizedString::kAXMediaCastOnButton:
-      return IDS_AX_MEDIA_CAST_ON_BUTTON;
-    case WebLocalizedString::kAXMediaDownloadButton:
-      return IDS_AX_MEDIA_DOWNLOAD_BUTTON;
-    case WebLocalizedString::kAXMediaOverflowButton:
-      return IDS_AX_MEDIA_OVERFLOW_BUTTON;
-    case WebLocalizedString::kAXMediaAudioElementHelp:
-      return IDS_AX_MEDIA_AUDIO_ELEMENT_HELP;
-    case WebLocalizedString::kAXMediaVideoElementHelp:
-      return IDS_AX_MEDIA_VIDEO_ELEMENT_HELP;
-    case WebLocalizedString::kAXMediaAudioSliderHelp:
-      return IDS_AX_MEDIA_AUDIO_SLIDER_HELP;
-    case WebLocalizedString::kAXMediaVideoSliderHelp:
-      return IDS_AX_MEDIA_VIDEO_SLIDER_HELP;
-    case WebLocalizedString::kAXMediaVolumeSliderHelp:
-      return IDS_AX_MEDIA_VOLUME_SLIDER_HELP;
-    case WebLocalizedString::kAXMediaCurrentTimeDisplayHelp:
-      return IDS_AX_MEDIA_CURRENT_TIME_DISPLAY_HELP;
-    case WebLocalizedString::kAXMediaTimeRemainingDisplayHelp:
-      return IDS_AX_MEDIA_TIME_REMAINING_DISPLAY_HELP;
-    case WebLocalizedString::kAXMediaOverflowButtonHelp:
-      return IDS_AX_MEDIA_OVERFLOW_BUTTON_HELP;
-    case WebLocalizedString::kAXMediaTouchLessPlayPauseAction:
-      return IDS_AX_MEDIA_TOUCHLESS_PLAY_PAUSE_ACTION;
-    case WebLocalizedString::kAXMediaTouchLessSeekAction:
-      return IDS_AX_MEDIA_TOUCHLESS_SEEK_ACTION;
-    case WebLocalizedString::kAXMediaTouchLessVolumeAction:
-      return IDS_AX_MEDIA_TOUCHLESS_VOLUME_ACTION;
     case WebLocalizedString::kDetailsLabel:
       return IDS_DETAILS_WITHOUT_SUMMARY_LABEL;
     case WebLocalizedString::kInputElementAltText:
       return IDS_FORM_INPUT_ALT;
     case WebLocalizedString::kMissingPluginText:
       return IDS_PLUGIN_INITIALIZATION_ERROR;
-    case WebLocalizedString::kAXMediaPlaybackError:
-      return IDS_MEDIA_PLAYBACK_ERROR;
     case WebLocalizedString::kMediaScrubbingMessageText:
       return IDS_MEDIA_SCRUBBING_MESSAGE_TEXT;
     case WebLocalizedString::kMultipleFileUploadText:
@@ -188,52 +122,12 @@
       return IDS_FORM_RESET_LABEL;
     case WebLocalizedString::kSubmitButtonDefaultLabel:
       return IDS_FORM_SUBMIT_LABEL;
-    case WebLocalizedString::kValidationBadInputForDateTime:
-      return IDS_FORM_VALIDATION_BAD_INPUT_DATETIME;
-    case WebLocalizedString::kValidationBadInputForNumber:
-      return IDS_FORM_VALIDATION_BAD_INPUT_NUMBER;
-    case WebLocalizedString::kValidationPatternMismatch:
-      return IDS_FORM_VALIDATION_PATTERN_MISMATCH;
-    case WebLocalizedString::kValidationRangeOverflowDateTime:
-      return IDS_FORM_VALIDATION_RANGE_OVERFLOW_DATETIME;
-    case WebLocalizedString::kValidationRangeUnderflowDateTime:
-      return IDS_FORM_VALIDATION_RANGE_UNDERFLOW_DATETIME;
     case WebLocalizedString::kValidationStepMismatchCloseToLimit:
       return IDS_FORM_VALIDATION_STEP_MISMATCH_CLOSE_TO_LIMIT;
     case WebLocalizedString::kValidationTooShort:
       return IDS_FORM_VALIDATION_TOO_SHORT;
     case WebLocalizedString::kValidationTooShortPlural:
       return IDS_FORM_VALIDATION_TOO_SHORT_PLURAL;
-    case WebLocalizedString::kValidationTypeMismatch:
-      return IDS_FORM_VALIDATION_TYPE_MISMATCH;
-    case WebLocalizedString::kValidationTypeMismatchForEmail:
-      return IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL;
-    case WebLocalizedString::kValidationTypeMismatchForEmailEmpty:
-      return IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_EMPTY;
-    case WebLocalizedString::kValidationTypeMismatchForEmailEmptyDomain:
-      return IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_EMPTY_DOMAIN;
-    case WebLocalizedString::kValidationTypeMismatchForEmailEmptyLocal:
-      return IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_EMPTY_LOCAL;
-    case WebLocalizedString::kValidationTypeMismatchForEmailInvalidDomain:
-      return IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_INVALID_DOMAIN;
-    case WebLocalizedString::kValidationTypeMismatchForEmailInvalidDots:
-      return IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_INVALID_DOTS;
-    case WebLocalizedString::kValidationTypeMismatchForEmailInvalidLocal:
-      return IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_INVALID_LOCAL;
-    case WebLocalizedString::kValidationTypeMismatchForEmailNoAtSign:
-      return IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_NO_AT_SIGN;
-    case WebLocalizedString::kValidationTypeMismatchForMultipleEmail:
-      return IDS_FORM_VALIDATION_TYPE_MISMATCH_MULTIPLE_EMAIL;
-    case WebLocalizedString::kValidationTypeMismatchForURL:
-      return IDS_FORM_VALIDATION_TYPE_MISMATCH_URL;
-    case WebLocalizedString::kValidationValueMissingForCheckbox:
-      return IDS_FORM_VALIDATION_VALUE_MISSING_CHECKBOX;
-    case WebLocalizedString::kValidationValueMissingForFile:
-      return IDS_FORM_VALIDATION_VALUE_MISSING_FILE;
-    case WebLocalizedString::kValidationValueMissingForMultipleFile:
-      return IDS_FORM_VALIDATION_VALUE_MISSING_MULTIPLE_FILE;
-    case WebLocalizedString::kValidationValueMissingForRadio:
-      return IDS_FORM_VALIDATION_VALUE_MISSING_RADIO;
     case WebLocalizedString::kWeekNumberLabel:
       return IDS_FORM_WEEK_NUMBER_LABEL;
     case WebLocalizedString::kTextTracksNoLabel:
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index e9e4ce0..f11bdc7b 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -63,6 +63,7 @@
 #include "ipc/ipc_sync_message_filter.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
@@ -237,7 +238,8 @@
 
 class ChannelBootstrapFilter : public ConnectionFilter {
  public:
-  explicit ChannelBootstrapFilter(IPC::mojom::ChannelBootstrapPtrInfo bootstrap)
+  explicit ChannelBootstrapFilter(
+      mojo::PendingRemote<IPC::mojom::ChannelBootstrap> bootstrap)
       : bootstrap_(std::move(bootstrap)) {}
 
  private:
@@ -253,13 +255,13 @@
 
     if (interface_name == IPC::mojom::ChannelBootstrap::Name_) {
       DCHECK(bootstrap_.is_valid());
-      mojo::FuseInterface(
-          IPC::mojom::ChannelBootstrapRequest(std::move(*interface_pipe)),
-          std::move(bootstrap_));
+      mojo::FusePipes(mojo::PendingReceiver<IPC::mojom::ChannelBootstrap>(
+                          std::move(*interface_pipe)),
+                      std::move(bootstrap_));
     }
   }
 
-  IPC::mojom::ChannelBootstrapPtrInfo bootstrap_;
+  mojo::PendingRemote<IPC::mojom::ChannelBootstrap> bootstrap_;
 
   DISALLOW_COPY_AND_ASSIGN(ChannelBootstrapFilter);
 };
@@ -554,11 +556,11 @@
 
 void ChildThreadImpl::ConnectChannel() {
   DCHECK(service_manager_connection_);
-  IPC::mojom::ChannelBootstrapPtr bootstrap;
+  mojo::PendingRemote<IPC::mojom::ChannelBootstrap> bootstrap;
   mojo::ScopedMessagePipeHandle handle =
-      mojo::MakeRequest(&bootstrap).PassMessagePipe();
+      bootstrap.InitWithNewPipeAndPassReceiver().PassPipe();
   service_manager_connection_->AddConnectionFilter(
-      std::make_unique<ChannelBootstrapFilter>(bootstrap.PassInterface()));
+      std::make_unique<ChannelBootstrapFilter>(std::move(bootstrap)));
 
   channel_->Init(
       IPC::ChannelMojo::CreateClientFactory(
diff --git a/content/common/media/peer_connection_tracker.mojom b/content/common/media/peer_connection_tracker.mojom
index 12fe034f..020e836 100644
--- a/content/common/media/peer_connection_tracker.mojom
+++ b/content/common/media/peer_connection_tracker.mojom
@@ -4,6 +4,21 @@
 
 module content.mojom;
 
+struct PeerConnectionInfo {
+  // ID of the peer connection. Unique only within the renderer process.
+  int32 lid;
+
+  // Serialized version of RTCConfiguration.
+  string rtc_configuration;
+
+  // Serialized version of blink::WebMediaConstraints.
+  string constraints;
+
+  // The URL of the blink::WebLocalFrame within which this peer connection
+  // lives. Used for debugging purposes (displayed by WebRTC-Internals).
+  string url;
+};
+
 // This interface allows forwarding PeerConnection events to WebRTCInternals in
 // the browser process.
 interface PeerConnectionTrackerHost {
@@ -11,6 +26,10 @@
   // TODO(vm.arjun): Migrate rest of the messages,
   // https://bugs.chromium.org/p/chromium/issues/detail?id=792801
 
+  // Notifies WebRTCInternals about the addition of the peer connection
+  // whose data is specified in |info|.
+  AddPeerConnection(PeerConnectionInfo info);
+
   // Notifies WebRTCInternals about the removal of the peer connection
   // identified with local id |lid|.
   RemovePeerConnection(int32 lid);
diff --git a/content/common/media/peer_connection_tracker_messages.h b/content/common/media/peer_connection_tracker_messages.h
index 231725e..0c721cb 100644
--- a/content/common/media/peer_connection_tracker_messages.h
+++ b/content/common/media/peer_connection_tracker_messages.h
@@ -14,21 +14,7 @@
 #define IPC_MESSAGE_EXPORT CONTENT_EXPORT
 #define IPC_MESSAGE_START PeerConnectionTrackerMsgStart
 
-IPC_STRUCT_BEGIN(PeerConnectionInfo)
-  // ID of the peer connection. Unique only within the renderer process.
-  IPC_STRUCT_MEMBER(int, lid)
-  // Serialized version of RTCConfiguration.
-  IPC_STRUCT_MEMBER(std::string, rtc_configuration)
-  // Serialized version of blink::WebMediaConstraints.
-  IPC_STRUCT_MEMBER(std::string, constraints)
-  // The URL of the blink::WebLocalFrame within which this peer connection
-  // lives. Used for debugging purposes (displayed by WebRTC-Internals).
-  IPC_STRUCT_MEMBER(std::string, url)
-IPC_STRUCT_END()
-
 // Messages sent from PeerConnectionTracker to PeerConnectionTrackerHost.
-IPC_MESSAGE_CONTROL1(PeerConnectionTrackerHost_AddPeerConnection,
-                     PeerConnectionInfo /* info */)
 IPC_MESSAGE_CONTROL2(PeerConnectionTrackerHost_AddStandardStats,
                      int /* lid */,
                      base::ListValue /* value */)
diff --git a/content/common/mime_sniffing_throttle.cc b/content/common/mime_sniffing_throttle.cc
index 62d72fe..b69db33 100644
--- a/content/common/mime_sniffing_throttle.cc
+++ b/content/common/mime_sniffing_throttle.cc
@@ -5,6 +5,8 @@
 #include "content/common/mime_sniffing_throttle.h"
 
 #include "content/common/mime_sniffing_url_loader.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/base/mime_sniffer.h"
 
 namespace content {
@@ -37,19 +39,19 @@
     // Pause the response until the mime type becomes ready.
     *defer = true;
 
-    network::mojom::URLLoaderPtr new_loader;
-    network::mojom::URLLoaderClientRequest new_loader_request;
+    mojo::PendingRemote<network::mojom::URLLoader> new_remote;
+    mojo::PendingReceiver<network::mojom::URLLoaderClient> new_receiver;
     network::mojom::URLLoaderPtr source_loader;
     network::mojom::URLLoaderClientRequest source_client_request;
     MimeSniffingURLLoader* mime_sniffing_loader;
-    std::tie(new_loader, new_loader_request, mime_sniffing_loader) =
+    std::tie(new_remote, new_receiver, mime_sniffing_loader) =
         MimeSniffingURLLoader::CreateLoader(weak_factory_.GetWeakPtr(),
                                             response_url, *response_head,
                                             task_runner_);
-    delegate_->InterceptResponse(std::move(new_loader),
-                                 std::move(new_loader_request), &source_loader,
-                                 &source_client_request);
-    mime_sniffing_loader->Start(std::move(source_loader),
+    delegate_->InterceptResponse(
+        network::mojom::URLLoaderPtr(std::move(new_remote)),
+        std::move(new_receiver), &source_loader, &source_client_request);
+    mime_sniffing_loader->Start(source_loader.PassInterface(),
                                 std::move(source_client_request));
   }
 }
diff --git a/content/common/mime_sniffing_throttle_unittest.cc b/content/common/mime_sniffing_throttle_unittest.cc
index d216feb..5ff0c32 100644
--- a/content/common/mime_sniffing_throttle_unittest.cc
+++ b/content/common/mime_sniffing_throttle_unittest.cc
@@ -10,6 +10,9 @@
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "content/common/mime_sniffing_url_loader.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "services/network/test/test_url_loader_client.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -106,8 +109,13 @@
     ASSERT_TRUE(mojo::FuseInterface(
         std::move(new_client_request),
         destination_loader_client_.CreateInterfacePtr().PassInterface()));
-    source_loader_request_ = mojo::MakeRequest(original_loader);
-    *original_client_request = mojo::MakeRequest(&source_loader_client_ptr_);
+
+    mojo::PendingRemote<network::mojom::URLLoader> pending_remote;
+    pending_receiver_ = pending_remote.InitWithNewPipeAndPassReceiver();
+    original_loader->Bind(std::move(pending_remote));
+
+    *original_client_request =
+        source_loader_client_remote_.BindNewPipeAndPassReceiver();
   }
 
   void LoadResponseBody(const std::string& body) {
@@ -116,7 +124,8 @@
       mojo::ScopedDataPipeConsumerHandle consumer;
       EXPECT_EQ(MOJO_RESULT_OK,
                 mojo::CreateDataPipe(nullptr, &source_body_handle_, &consumer));
-      source_loader_client()->OnStartLoadingResponseBody(std::move(consumer));
+      source_loader_client_remote()->OnStartLoadingResponseBody(
+          std::move(consumer));
     }
 
     MojoDataPipeSender sender(std::move(source_body_handle_));
@@ -129,7 +138,8 @@
   }
 
   void CompleteResponse() {
-    source_loader_client()->OnComplete(network::URLLoaderCompletionStatus());
+    source_loader_client_remote()->OnComplete(
+        network::URLLoaderCompletionStatus());
     source_body_handle_.reset();
   }
 
@@ -162,8 +172,8 @@
     return &destination_loader_client_;
   }
 
-  network::mojom::URLLoaderClient* source_loader_client() {
-    return source_loader_client_ptr_.get();
+  mojo::Remote<network::mojom::URLLoaderClient>& source_loader_client_remote() {
+    return source_loader_client_remote_;
   }
 
  private:
@@ -175,9 +185,9 @@
   network::mojom::URLLoaderPtr destination_loader_ptr_;
   network::TestURLLoaderClient destination_loader_client_;
 
-  // A pair of a loader and a loader client for source of the response.
-  network::mojom::URLLoaderClientPtr source_loader_client_ptr_;
-  network::mojom::URLLoaderRequest source_loader_request_;
+  // A pair of a receiver and a remote for source of the response.
+  mojo::PendingReceiver<network::mojom::URLLoader> pending_receiver_;
+  mojo::Remote<network::mojom::URLLoaderClient> source_loader_client_remote_;
 
   mojo::ScopedDataPipeProducerHandle source_body_handle_;
 };
@@ -308,7 +318,7 @@
   EXPECT_TRUE(delegate->is_intercepted());
 
   // Call OnComplete() without sending body.
-  delegate->source_loader_client()->OnComplete(
+  delegate->source_loader_client_remote()->OnComplete(
       network::URLLoaderCompletionStatus(net::ERR_FAILED));
   delegate->destination_loader_client()->RunUntilComplete();
 
@@ -332,11 +342,11 @@
   EXPECT_TRUE(delegate->is_intercepted());
 
   mojo::DataPipe pipe;
-  delegate->source_loader_client()->OnStartLoadingResponseBody(
+  delegate->source_loader_client_remote()->OnStartLoadingResponseBody(
       std::move(pipe.consumer_handle));
   pipe.producer_handle.reset();  // The pipe is empty.
 
-  delegate->source_loader_client()->OnComplete(
+  delegate->source_loader_client_remote()->OnComplete(
       network::URLLoaderCompletionStatus());
   delegate->destination_loader_client()->RunUntilComplete();
 
diff --git a/content/common/mime_sniffing_url_loader.cc b/content/common/mime_sniffing_url_loader.cc
index 7d9c7163..51b1811 100644
--- a/content/common/mime_sniffing_url_loader.cc
+++ b/content/common/mime_sniffing_url_loader.cc
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "content/common/mime_sniffing_throttle.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "net/base/mime_sniffer.h"
 
 namespace content {
@@ -15,35 +15,38 @@
 const char MimeSniffingURLLoader::kDefaultMimeType[] = "text/plain";
 
 // static
-std::tuple<network::mojom::URLLoaderPtr,
-           network::mojom::URLLoaderClientRequest,
+std::tuple<mojo::PendingRemote<network::mojom::URLLoader>,
+           mojo::PendingReceiver<network::mojom::URLLoaderClient>,
            MimeSniffingURLLoader*>
 MimeSniffingURLLoader::CreateLoader(
     base::WeakPtr<MimeSniffingThrottle> throttle,
     const GURL& response_url,
     const network::ResourceResponseHead& response_head,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  network::mojom::URLLoaderPtr url_loader;
-  network::mojom::URLLoaderClientPtr url_loader_client;
-  network::mojom::URLLoaderClientRequest url_loader_client_request =
-      mojo::MakeRequest(&url_loader_client);
+  mojo::PendingRemote<network::mojom::URLLoader> url_loader;
+  mojo::PendingRemote<network::mojom::URLLoaderClient> url_loader_client;
+  mojo::PendingReceiver<network::mojom::URLLoaderClient>
+      url_loader_client_receiver =
+          url_loader_client.InitWithNewPipeAndPassReceiver();
+
   auto loader = base::WrapUnique(new MimeSniffingURLLoader(
       std::move(throttle), response_url, response_head,
       std::move(url_loader_client), std::move(task_runner)));
   MimeSniffingURLLoader* loader_rawptr = loader.get();
-  mojo::MakeStrongBinding(std::move(loader), mojo::MakeRequest(&url_loader));
+  mojo::MakeSelfOwnedReceiver(std::move(loader),
+                              url_loader.InitWithNewPipeAndPassReceiver());
   return std::make_tuple(std::move(url_loader),
-                         std::move(url_loader_client_request), loader_rawptr);
+                         std::move(url_loader_client_receiver), loader_rawptr);
 }
 
 MimeSniffingURLLoader::MimeSniffingURLLoader(
     base::WeakPtr<MimeSniffingThrottle> throttle,
     const GURL& response_url,
     const network::ResourceResponseHead& response_head,
-    network::mojom::URLLoaderClientPtr destination_url_loader_client,
+    mojo::PendingRemote<network::mojom::URLLoaderClient>
+        destination_url_loader_client,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
     : throttle_(throttle),
-      source_url_client_binding_(this),
       destination_url_loader_client_(std::move(destination_url_loader_client)),
       response_url_(response_url),
       response_head_(response_head),
@@ -58,11 +61,12 @@
 MimeSniffingURLLoader::~MimeSniffingURLLoader() = default;
 
 void MimeSniffingURLLoader::Start(
-    network::mojom::URLLoaderPtr source_url_loader,
-    network::mojom::URLLoaderClientRequest source_url_loader_client_request) {
-  source_url_loader_ = std::move(source_url_loader);
-  source_url_client_binding_.Bind(std::move(source_url_loader_client_request),
-                                  task_runner_);
+    mojo::PendingRemote<network::mojom::URLLoader> source_url_loader_remote,
+    mojo::PendingReceiver<network::mojom::URLLoaderClient>
+        source_url_client_receiver) {
+  source_url_loader_.Bind(std::move(source_url_loader_remote));
+  source_url_client_receiver_.Bind(std::move(source_url_client_receiver),
+                                   task_runner_);
 }
 
 void MimeSniffingURLLoader::OnReceiveResponse(
@@ -359,7 +363,7 @@
   body_consumer_watcher_.Cancel();
   body_producer_watcher_.Cancel();
   source_url_loader_.reset();
-  source_url_client_binding_.Close();
+  source_url_client_receiver_.reset();
   destination_url_loader_client_.reset();
   // |this| should be removed since the owner will destroy |this| or the owner
   // has already been destroyed by some reason.
diff --git a/content/common/mime_sniffing_url_loader.h b/content/common/mime_sniffing_url_loader.h
index 54801915..a04f6bdc 100644
--- a/content/common/mime_sniffing_url_loader.h
+++ b/content/common/mime_sniffing_url_loader.h
@@ -13,6 +13,10 @@
 #include "base/strings/string_piece.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
 #include "services/network/public/cpp/resource_response.h"
@@ -58,12 +62,14 @@
 
   // Start waiting for the body.
   void Start(
-      network::mojom::URLLoaderPtr source_url_loader,
-      network::mojom::URLLoaderClientRequest source_url_loader_client_request);
+      mojo::PendingRemote<network::mojom::URLLoader> source_url_loader_remote,
+      mojo::PendingReceiver<network::mojom::URLLoaderClient>
+          source_url_client_receiver);
 
-  // network::mojom::URLLoaderPtr controls the lifetime of the loader.
-  static std::tuple<network::mojom::URLLoaderPtr,
-                    network::mojom::URLLoaderClientRequest,
+  // mojo::PendingRemote<network::mojom::URLLoader> controls the lifetime of the
+  // loader.
+  static std::tuple<mojo::PendingRemote<network::mojom::URLLoader>,
+                    mojo::PendingReceiver<network::mojom::URLLoaderClient>,
                     MimeSniffingURLLoader*>
   CreateLoader(base::WeakPtr<MimeSniffingThrottle> throttle,
                const GURL& response_url,
@@ -75,7 +81,8 @@
       base::WeakPtr<MimeSniffingThrottle> throttle,
       const GURL& response_url,
       const network::ResourceResponseHead& response_head,
-      network::mojom::URLLoaderClientPtr destination_url_loader_client,
+      mojo::PendingRemote<network::mojom::URLLoaderClient>
+          destination_url_loader_client,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
   // network::mojom::URLLoaderClient implementation (called from the source of
@@ -117,9 +124,10 @@
 
   base::WeakPtr<MimeSniffingThrottle> throttle_;
 
-  mojo::Binding<network::mojom::URLLoaderClient> source_url_client_binding_;
-  network::mojom::URLLoaderPtr source_url_loader_;
-  network::mojom::URLLoaderClientPtr destination_url_loader_client_;
+  mojo::Receiver<network::mojom::URLLoaderClient> source_url_client_receiver_{
+      this};
+  mojo::Remote<network::mojom::URLLoader> source_url_loader_;
+  mojo::Remote<network::mojom::URLLoaderClient> destination_url_loader_client_;
 
   GURL response_url_;
 
diff --git a/content/common/navigation_params.mojom b/content/common/navigation_params.mojom
index c0bbef7..e2761bd 100644
--- a/content/common/navigation_params.mojom
+++ b/content/common/navigation_params.mojom
@@ -344,4 +344,8 @@
   // https://wicg.github.io/cors-rfc1918/#address-space
   network.mojom.IPAddressSpace ip_address_space =
       network.mojom.IPAddressSpace.kUnknown;
+
+  // The base URL which will be set for the document to support relative path
+  // subresource loading in unsigned bundled exchanges file.
+  url.mojom.Url base_url_override_for_bundled_exchanges;
 };
diff --git a/content/public/app/content_browser_manifest.cc b/content/public/app/content_browser_manifest.cc
index ce5dd0d84..ddeacb3 100644
--- a/content/public/app/content_browser_manifest.cc
+++ b/content/public/app/content_browser_manifest.cc
@@ -222,13 +222,11 @@
                   "blink.mojom.GeolocationService",
                   "blink.mojom.IDBFactory",
                   "blink.mojom.InsecureInputService",
-                  "blink.mojom.KeyboardLockService",
                   "blink.mojom.MediaDevicesDispatcherHost",
                   "blink.mojom.MediaStreamDispatcherHost",
                   "blink.mojom.MediaSessionService",
                   "blink.mojom.NativeFileSystemManager",
                   "blink.mojom.NotificationService",
-                  "blink.mojom.PictureInPictureService",
                   "blink.mojom.Portal",
                   "blink.mojom.PrefetchURLLoaderService",
                   "blink.mojom.QuotaDispatcherHost",
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.cc b/content/renderer/media/webrtc/peer_connection_tracker.cc
index c8e1637c..ccb773f 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker.cc
@@ -708,19 +708,21 @@
   DCHECK(pc_handler);
   DCHECK_EQ(GetLocalIDForHandler(pc_handler), -1);
   DVLOG(1) << "PeerConnectionTracker::RegisterPeerConnection()";
-  PeerConnectionInfo info;
+  auto info = mojom::PeerConnectionInfo::New();
 
-  info.lid = GetNextLocalID();
-  info.rtc_configuration = SerializeConfiguration(config);
+  info->lid = GetNextLocalID();
+  info->rtc_configuration = SerializeConfiguration(config);
 
-  info.constraints = SerializeMediaConstraints(constraints);
+  info->constraints = SerializeMediaConstraints(constraints);
   if (frame)
-    info.url = frame->GetDocument().Url().GetString().Utf8();
+    info->url = frame->GetDocument().Url().GetString().Utf8();
   else
-    info.url = "test:testing";
-  SendTarget()->Send(new PeerConnectionTrackerHost_AddPeerConnection(info));
+    info->url = "test:testing";
 
-  peer_connection_local_id_map_.insert(std::make_pair(pc_handler, info.lid));
+  int32_t lid = info->lid;
+  GetPeerConnectionTrackerHost()->AddPeerConnection(std::move(info));
+
+  peer_connection_local_id_map_.insert(std::make_pair(pc_handler, lid));
 }
 
 void PeerConnectionTracker::UnregisterPeerConnection(
diff --git a/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc b/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
index 2d9a5a3e..c6e2b52 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
@@ -60,6 +60,7 @@
   MockPeerConnectionTrackerHost() : binding_(this) {}
   MOCK_METHOD3(UpdatePeerConnection,
                void(int, const std::string&, const std::string&));
+  MOCK_METHOD1(AddPeerConnection, void(mojom::PeerConnectionInfoPtr));
   MOCK_METHOD1(RemovePeerConnection, void(int));
   MOCK_METHOD2(OnPeerConnectionSessionIdSet, void(int, const std::string&));
   MOCK_METHOD5(GetUserMedia,
@@ -119,24 +120,6 @@
 
 namespace {
 
-class MockSendTargetThread : public MockRenderThread {
- public:
-  MOCK_METHOD1(OnAddPeerConnection, void(PeerConnectionInfo));
-
- private:
-  bool OnMessageReceived(const IPC::Message& msg) override;
-};
-
-bool MockSendTargetThread::OnMessageReceived(const IPC::Message& msg) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(MockSendTargetThread, msg)
-    IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_AddPeerConnection,
-                        OnAddPeerConnection)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
 // TODO(https://crbug.com/868868): Move this into a separate file.
 class MockPeerConnectionHandler : public RTCPeerConnectionHandler {
  public:
@@ -159,24 +142,22 @@
     tracker_.reset(new PeerConnectionTracker(
         mock_host_->CreateInterfacePtrAndBind(),
         blink::scheduler::GetSingleThreadTaskRunnerForTesting()));
-    target_thread_.reset(new MockSendTargetThread());
-    tracker_->OverrideSendTargetForTesting(target_thread_.get());
   }
 
   void CreateAndRegisterPeerConnectionHandler() {
     mock_handler_.reset(new MockPeerConnectionHandler());
-    EXPECT_CALL(*target_thread_, OnAddPeerConnection(_));
+    EXPECT_CALL(*mock_host_, AddPeerConnection(_));
     tracker_->RegisterPeerConnection(
         mock_handler_.get(),
         webrtc::PeerConnectionInterface::RTCConfiguration(),
         blink::WebMediaConstraints(), nullptr);
+    task_environment_.RunUntilIdle();
   }
 
  protected:
   base::test::SingleThreadTaskEnvironment task_environment_;
   std::unique_ptr<MockPeerConnectionTrackerHost> mock_host_;
   std::unique_ptr<PeerConnectionTracker> tracker_;
-  std::unique_ptr<MockSendTargetThread> target_thread_;
   std::unique_ptr<MockPeerConnectionHandler> mock_handler_;
 };
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 7daa962a..f79ba91 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -511,6 +511,8 @@
   }
 
   navigation_params->had_transient_activation = common_params.has_user_gesture;
+  navigation_params->base_url_override_for_bundled_exchanges =
+      commit_params.base_url_override_for_bundled_exchanges;
 }
 
 mojom::CommonNavigationParamsPtr MakeCommonNavigationParams(
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
index 4571f31..009e06d 100644
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
+++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
@@ -104,25 +104,20 @@
   // when the worker is not installed, or the worker is launched for checking
   // the update.
   if (params->installed_scripts_info) {
-    installed_scripts_manager_params = std::make_unique<
-        blink::WebServiceWorkerInstalledScriptsManagerParams>();
-    installed_scripts_manager_params->installed_scripts_urls =
-        std::move(params->installed_scripts_info->installed_urls);
-    installed_scripts_manager_params->manager_receiver =
-        params->installed_scripts_info->manager_receiver.PassPipe();
-    installed_scripts_manager_params->manager_host_remote =
-        params->installed_scripts_info->manager_host_remote.PassPipe();
-    DCHECK(installed_scripts_manager_params->manager_receiver.is_valid());
-    DCHECK(installed_scripts_manager_params->manager_host_remote.is_valid());
+    installed_scripts_manager_params =
+        std::make_unique<blink::WebServiceWorkerInstalledScriptsManagerParams>(
+            std::move(params->installed_scripts_info->installed_urls),
+            params->installed_scripts_info->manager_receiver.PassPipe(),
+            params->installed_scripts_info->manager_host_remote.PassPipe());
   }
 
-  auto worker = blink::WebEmbeddedWorker::Create(
-      service_worker_context_client_.get(), cache_storage.PassPipe(),
-      interface_provider.PassHandle(), browser_interface_broker.PassPipe());
+  auto worker =
+      blink::WebEmbeddedWorker::Create(service_worker_context_client_.get());
   service_worker_context_client_->StartWorkerContextOnInitiatorThread(
       std::move(worker), std::move(start_data),
       std::move(installed_scripts_manager_params),
-      params->content_settings_proxy.PassHandle());
+      params->content_settings_proxy.PassHandle(), cache_storage.PassPipe(),
+      interface_provider.PassHandle(), browser_interface_broker.PassPipe());
 }
 
 void EmbeddedWorkerInstanceClientImpl::StopWorker() {
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index 4436a977..9552400 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -174,12 +174,17 @@
     std::unique_ptr<blink::WebEmbeddedWorkerStartData> start_data,
     std::unique_ptr<blink::WebServiceWorkerInstalledScriptsManagerParams>
         installed_scripts_manager_params,
-    mojo::ScopedMessagePipeHandle content_settings_handle) {
+    mojo::ScopedMessagePipeHandle content_settings_handle,
+    mojo::ScopedMessagePipeHandle cache_storage,
+    mojo::ScopedMessagePipeHandle interface_provider,
+    mojo::ScopedMessagePipeHandle browser_interface_broker) {
   DCHECK(initiator_thread_task_runner_->RunsTasksInCurrentSequence());
   worker_ = std::move(worker);
   worker_->StartWorkerContext(
       std::move(start_data), std::move(installed_scripts_manager_params),
-      std::move(content_settings_handle), initiator_thread_task_runner_);
+      std::move(content_settings_handle), std::move(cache_storage),
+      std::move(interface_provider), std::move(browser_interface_broker),
+      initiator_thread_task_runner_);
 }
 
 blink::WebEmbeddedWorker& ServiceWorkerContextClient::worker() {
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index 99c9034..064263f 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -117,7 +117,10 @@
       std::unique_ptr<blink::WebEmbeddedWorker> worker,
       std::unique_ptr<blink::WebEmbeddedWorkerStartData> start_data,
       std::unique_ptr<blink::WebServiceWorkerInstalledScriptsManagerParams>,
-      mojo::ScopedMessagePipeHandle content_settings_handle);
+      mojo::ScopedMessagePipeHandle content_settings_handle,
+      mojo::ScopedMessagePipeHandle cache_storage,
+      mojo::ScopedMessagePipeHandle interface_provider,
+      mojo::ScopedMessagePipeHandle browser_interface_broker);
   // Called on the initiator thread.
   blink::WebEmbeddedWorker& worker();
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 4f1be9a..c1ee8e1 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1850,6 +1850,7 @@
     "../browser/web_contents/web_drag_source_mac_unittest.mm",
     "../browser/web_package/bundled_exchanges_reader_unittest.cc",
     "../browser/web_package/bundled_exchanges_url_loader_factory_unittest.cc",
+    "../browser/web_package/bundled_exchanges_utils_unittest.cc",
     "../browser/web_package/signed_exchange_cert_fetcher_unittest.cc",
     "../browser/web_package/signed_exchange_certificate_chain_unittest.cc",
     "../browser/web_package/signed_exchange_envelope_unittest.cc",
diff --git a/content/test/data/back_forward_cache/empty.js b/content/test/data/back_forward_cache/empty.js
new file mode 100644
index 0000000..8b1a393
--- /dev/null
+++ b/content/test/data/back_forward_cache/empty.js
@@ -0,0 +1 @@
+// empty
diff --git a/content/test/data/back_forward_cache/page_with_shared_worker.html b/content/test/data/back_forward_cache/page_with_shared_worker.html
new file mode 100644
index 0000000..82cf488
--- /dev/null
+++ b/content/test/data/back_forward_cache/page_with_shared_worker.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<body>This page has a shared worker.</body>
+  <script>
+    const worker = new SharedWorker('/back_forward_cache/shared_worker.js');
+  </script>
+</html>
diff --git a/content/test/data/back_forward_cache/shared_worker.js b/content/test/data/back_forward_cache/shared_worker.js
new file mode 100644
index 0000000..8b1a393
--- /dev/null
+++ b/content/test/data/back_forward_cache/shared_worker.js
@@ -0,0 +1 @@
+// empty
diff --git a/content/test/data/bundled_exchanges/invalid_bundled_exchanges.wbn b/content/test/data/bundled_exchanges/invalid_bundled_exchanges.wbn
new file mode 100644
index 0000000..d133559
--- /dev/null
+++ b/content/test/data/bundled_exchanges/invalid_bundled_exchanges.wbn
@@ -0,0 +1 @@
+This is an invalid bundled exchanges file.
diff --git a/docs/sheriff.md b/docs/sheriff.md
index a56b939..7971bce 100644
--- a/docs/sheriff.md
+++ b/docs/sheriff.md
@@ -174,7 +174,7 @@
 This gives you a UI listing the failures for the named test broken down by
 builder. At this point you should check to see where the flakes start
 revision-wise. If that helps you identify a culprit CL revert it and move on;
-otherwise, [disable the test][gtest-disable].
+otherwise, [disable the test][test-disable].
 
 For more advice on dealing with flaky tests, look at the "Test Failed" section
 below under "Diagnosing Build Failures".
@@ -210,7 +210,7 @@
 culprit CLs around there. In general, for a flaky test, you should either:
 
 * Revert a culprit CL, if you can find it, or
-* [Disable the test][gtest-disable] it as narrowly as possible to fix the flake
+* [Disable the test][test-disable] it as narrowly as possible to fix the flake
   (eg, if the test is broken on Windows, only disable it on Windows)
 
 For more advice on dealing with flaky tests, look at the "Test Failed" section
@@ -406,7 +406,7 @@
 [contacting-troopers]: https://chromium.googlesource.com/infra/infra/+/master/doc/users/contacting_troopers.md
 [get-the-code]: https://www.chromium.org/developers/how-tos/get-the-code
 [goma]: http://shortn/_Iox00npQJW
-[gtest-disable]: https://github.com/google/googletest/blob/master/googletest/docs/faq.md#how-do-i-temporarily-disable-a-test
+[test-disable]: https://www.chromium.org/developers/tree-sheriffs/sheriff-details-chromium#TOC-How-do-I-disable-a-flaky-test-
 [new flakiness dashboard]: https://analysis.chromium.org/p/chromium/flake-portal
 [old flakiness dashboard]: https://test-results.appspot.com/dashboards/flakiness_dashboard.html
 [sheriff-o-matic]: https://sheriff-o-matic.appspot.com/chromium
diff --git a/extensions/renderer/worker_thread_dispatcher.cc b/extensions/renderer/worker_thread_dispatcher.cc
index ec56597f..4aa3021 100644
--- a/extensions/renderer/worker_thread_dispatcher.cc
+++ b/extensions/renderer/worker_thread_dispatcher.cc
@@ -116,16 +116,9 @@
     CHECK(found);
     if (worker_thread_id == kMainThreadId)
       return false;
-    base::TaskRunner* runner = GetTaskRunnerFor(worker_thread_id);
-    // The TaskRunner may have been destroyed, for example, if the extension
-    // was unloaded, so check before trying to post a task.
-    if (runner == nullptr)
-      return false;
-    bool task_posted = runner->PostTask(
-        FROM_HERE, base::BindOnce(&WorkerThreadDispatcher::ForwardIPC,
-                                  worker_thread_id, message));
-    DCHECK(task_posted) << "Could not PostTask IPC to worker thread.";
-    return true;
+    return PostTaskToWorkerThread(
+        worker_thread_id, base::BindOnce(&WorkerThreadDispatcher::ForwardIPC,
+                                         worker_thread_id, message));
   }
   return false;
 }
@@ -150,6 +143,13 @@
     int worker_thread_id,
     const IPC::Message& message) {
   CHECK_EQ(content::WorkerThread::GetCurrentId(), worker_thread_id);
+
+  // If the worker state was already destroyed via
+  // Dispatcher::WillDestroyServiceWorkerContextOnWorkerThread, then
+  // drop this IPC. See https://crbug.com/1008143 for details.
+  if (!GetServiceWorkerData())
+    return;
+
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(WorkerThreadDispatcher, message)
     IPC_MESSAGE_HANDLER(ExtensionMsg_ResponseWorker, OnResponseWorker)
@@ -164,11 +164,16 @@
   CHECK(handled);
 }
 
-base::TaskRunner* WorkerThreadDispatcher::GetTaskRunnerFor(
-    int worker_thread_id) {
+bool WorkerThreadDispatcher::PostTaskToWorkerThread(int worker_thread_id,
+                                                    base::OnceClosure task) {
   base::AutoLock lock(task_runner_map_lock_);
   auto it = task_runner_map_.find(worker_thread_id);
-  return it == task_runner_map_.end() ? nullptr : it->second;
+  if (it == task_runner_map_.end())
+    return false;
+
+  bool task_posted = it->second->PostTask(FROM_HERE, std::move(task));
+  DCHECK(task_posted) << "Could not PostTask IPC to worker thread.";
+  return task_posted;
 }
 
 bool WorkerThreadDispatcher::Send(IPC::Message* message) {
diff --git a/extensions/renderer/worker_thread_dispatcher.h b/extensions/renderer/worker_thread_dispatcher.h
index b9b9970..dc341bb6 100644
--- a/extensions/renderer/worker_thread_dispatcher.h
+++ b/extensions/renderer/worker_thread_dispatcher.h
@@ -94,7 +94,7 @@
   void OnMessageReceivedOnWorkerThread(int worker_thread_id,
                                        const IPC::Message& message);
 
-  base::TaskRunner* GetTaskRunnerFor(int worker_thread_id);
+  bool PostTaskToWorkerThread(int worker_thread_id, base::OnceClosure task);
 
   // IPC handlers.
   void OnResponseWorker(int worker_thread_id,
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 736f8ca..495fac7 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -239,6 +239,7 @@
     "//ios/chrome/browser/ui/toolbar:toolbar_ui",
     "//ios/chrome/browser/ui/toolbar/public",
     "//ios/chrome/browser/ui/util",
+    "//ios/chrome/browser/ui/util:multiwindow_util",
     "//ios/chrome/browser/ui/webui:webui_internal",
     "//ios/chrome/browser/upgrade",
     "//ios/chrome/browser/url_loading",
diff --git a/ios/chrome/app/main_application_delegate.mm b/ios/chrome/app/main_application_delegate.mm
index bf2747f..e6b0ccc 100644
--- a/ios/chrome/app/main_application_delegate.mm
+++ b/ios/chrome/app/main_application_delegate.mm
@@ -4,6 +4,7 @@
 
 #import "ios/chrome/app/main_application_delegate.h"
 
+#include "base/ios/ios_util.h"
 #include "base/mac/foundation_util.h"
 #import "ios/chrome/app/application_delegate/app_navigation.h"
 #import "ios/chrome/app/application_delegate/app_state.h"
@@ -18,6 +19,9 @@
 #import "ios/chrome/app/chrome_overlay_window.h"
 #import "ios/chrome/app/main_application_delegate_testing.h"
 #import "ios/chrome/app/main_controller.h"
+#import "ios/chrome/browser/ui/main/scene_controller.h"
+#import "ios/chrome/browser/ui/main/scene_state.h"
+#include "ios/chrome/browser/ui/util/multi_window_support.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
 #import "ios/testing/perf/startupLoggers.h"
@@ -47,6 +51,14 @@
   id<TabSwitching> _tabSwitcherProtocol;
 }
 
+// The state representing the only "scene" on iOS 12. On iOS 13, only created
+// temporarily before multiwindow is fully implemented to also represent the
+// only scene.
+@property(nonatomic, strong) SceneState* sceneState;
+
+// The controller for |sceneState|.
+@property(nonatomic, strong) SceneController* sceneController;
+
 @end
 
 @implementation MainApplicationDelegate
@@ -66,6 +78,15 @@
     _tabSwitcherProtocol = _mainController;
     _appNavigation = _mainController;
     [_mainController setAppState:_appState];
+
+    if (!IsMultiwindowSupported()) {
+      // When multiwindow is not supported, this object holds a "scene" state
+      // and a "scene" controller. This allows the rest of the app to be mostly
+      // multiwindow-agnostic.
+      _sceneState = [[SceneState alloc] init];
+      _sceneController =
+          [[SceneController alloc] initWithSceneState:_sceneState];
+    }
   }
   return self;
 }
diff --git a/ios/chrome/browser/metrics/previous_session_info.h b/ios/chrome/browser/metrics/previous_session_info.h
index 316c588..021d227 100644
--- a/ios/chrome/browser/metrics/previous_session_info.h
+++ b/ios/chrome/browser/metrics/previous_session_info.h
@@ -90,6 +90,10 @@
 // is available.
 @property(nonatomic, strong, readonly) NSString* OSVersion;
 
+// The version of the previous session or nil if no previous session data is
+// available.
+@property(nonatomic, strong, readonly) NSString* previousSessionVersion;
+
 // The time at which the previous sesion ended. Note that this is only an
 // estimate and is updated whenever another value of the receiver is updated.
 @property(nonatomic, strong, readonly) NSDate* sessionEndTime;
diff --git a/ios/chrome/browser/metrics/previous_session_info.mm b/ios/chrome/browser/metrics/previous_session_info.mm
index 892e748..a8d73f4 100644
--- a/ios/chrome/browser/metrics/previous_session_info.mm
+++ b/ios/chrome/browser/metrics/previous_session_info.mm
@@ -113,6 +113,7 @@
 @property(nonatomic, assign) BOOL isFirstSessionAfterLanguageChange;
 @property(nonatomic, assign) BOOL OSRestartedAfterPreviousSession;
 @property(nonatomic, strong) NSString* OSVersion;
+@property(nonatomic, strong) NSString* previousSessionVersion;
 @property(nonatomic, strong) NSDate* sessionEndTime;
 
 @end
@@ -175,6 +176,8 @@
     gSharedInstance.OSVersion = versionOfOSAtLastRun;
 
     NSString* lastRanVersion = [defaults stringForKey:kLastRanVersion];
+    gSharedInstance.previousSessionVersion = lastRanVersion;
+
     NSString* currentVersion =
         base::SysUTF8ToNSString(version_info::GetVersionNumber());
     gSharedInstance.isFirstSessionAfterUpgrade =
diff --git a/ios/chrome/browser/metrics/previous_session_info_private.h b/ios/chrome/browser/metrics/previous_session_info_private.h
index fb7f5a1..00189a6 100644
--- a/ios/chrome/browser/metrics/previous_session_info_private.h
+++ b/ios/chrome/browser/metrics/previous_session_info_private.h
@@ -14,6 +14,7 @@
 @property(nonatomic, assign)
     previous_session_info_constants::DeviceBatteryState deviceBatteryState;
 @property(nonatomic, assign) BOOL OSRestartedAfterPreviousSession;
+@property(nonatomic, strong) NSString* previousSessionVersion;
 
 + (void)resetSharedInstanceForTesting;
 
diff --git a/ios/chrome/browser/metrics/previous_session_info_unittest.mm b/ios/chrome/browser/metrics/previous_session_info_unittest.mm
index b8c9e07..0a256e0 100644
--- a/ios/chrome/browser/metrics/previous_session_info_unittest.mm
+++ b/ios/chrome/browser/metrics/previous_session_info_unittest.mm
@@ -45,6 +45,7 @@
   EXPECT_FALSE([sharedInstance didSeeMemoryWarningShortlyBeforeTerminating]);
   EXPECT_TRUE([sharedInstance isFirstSessionAfterUpgrade]);
   EXPECT_TRUE([sharedInstance isFirstSessionAfterLanguageChange]);
+  EXPECT_FALSE([sharedInstance previousSessionVersion]);
 }
 
 TEST_F(PreviousSessionInfoTest, InitializationWithSameLanguage) {
@@ -96,6 +97,7 @@
   // Checks the values.
   EXPECT_FALSE([sharedInstance didSeeMemoryWarningShortlyBeforeTerminating]);
   EXPECT_FALSE([sharedInstance isFirstSessionAfterUpgrade]);
+  EXPECT_NSEQ(currentVersion, [sharedInstance previousSessionVersion]);
 }
 
 TEST_F(PreviousSessionInfoTest, InitializationWithSameVersionMemoryWarning) {
@@ -118,6 +120,7 @@
   // Checks the values.
   EXPECT_TRUE([sharedInstance didSeeMemoryWarningShortlyBeforeTerminating]);
   EXPECT_FALSE([sharedInstance isFirstSessionAfterUpgrade]);
+  EXPECT_NSEQ(currentVersion, [sharedInstance previousSessionVersion]);
 }
 
 TEST_F(PreviousSessionInfoTest, InitializationDifferentVersionNoMemoryWarning) {
@@ -135,6 +138,7 @@
   // Checks the values.
   EXPECT_FALSE([sharedInstance didSeeMemoryWarningShortlyBeforeTerminating]);
   EXPECT_TRUE([sharedInstance isFirstSessionAfterUpgrade]);
+  EXPECT_NSEQ(@"Fake Version", [sharedInstance previousSessionVersion]);
 }
 
 TEST_F(PreviousSessionInfoTest, InitializationDifferentVersionMemoryWarning) {
@@ -155,6 +159,7 @@
   // Checks the values.
   EXPECT_TRUE([sharedInstance didSeeMemoryWarningShortlyBeforeTerminating]);
   EXPECT_TRUE([sharedInstance isFirstSessionAfterUpgrade]);
+  EXPECT_NSEQ(@"Fake Version", [sharedInstance previousSessionVersion]);
 }
 
 // Creates conditions that exist on the first app run and tests
diff --git a/ios/chrome/browser/resources/Settings.bundle/Root.plist b/ios/chrome/browser/resources/Settings.bundle/Root.plist
index 7e7f08f72..0ccc2ba6 100644
--- a/ios/chrome/browser/resources/Settings.bundle/Root.plist
+++ b/ios/chrome/browser/resources/Settings.bundle/Root.plist
@@ -32,248 +32,6 @@
 			<key>File</key>
 			<string>Experimental</string>
 		</dict>
-		<!--  iOS13 is crashing on PSChildPaneSpecifier/File keys on devices.  Cut
-		      and paste everything in experimental settings to here for now. -->
-		<dict>
-			<key>Type</key>
-			<string>PSGroupSpecifier</string>
-			<key>Title</key>
-			<string>UI Settings</string>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSToggleSwitchSpecifier</string>
-			<key>Title</key>
-			<string>Enable Pseudo-RTL</string>
-			<key>Key</key>
-			<string>EnablePseudoRTL</string>
-			<key>DefaultValue</key>
-			<false/>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSToggleSwitchSpecifier</string>
-			<key>Title</key>
-			<string>Force First Run</string>
-			<key>Key</key>
-			<string>FirstRunForceEnabled</string>
-			<key>DefaultValue</key>
-			<false/>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSMultiValueSpecifier</string>
-			<key>Title</key>
-			<string>Force What&apos;s New Promo</string>
-			<key>Key</key>
-			<string>WhatsNewPromoStatus</string>
-			<key>DefaultValue</key>
-			<integer>0</integer>
-			<key>Values</key>
-			<array>
-				<integer>0</integer>
-				<integer>1</integer>
-				<integer>2</integer>
-				<integer>3</integer>
-			</array>
-			<key>Titles</key>
-			<array>
-				<string>Default</string>
-				<string>Tip Command Test</string>
-				<string>Tip Move to Dock Promo</string>
-				<string>Tip Review Updated ToS</string>
-			</array>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSMultiValueSpecifier</string>
-			<key>Title</key>
-			<string>Force Language</string>
-			<key>Key</key>
-			<string>UILanguageOverride</string>
-			<key>DefaultValue</key>
-			<string></string>
-			<key>Values</key>
-			<array>
-				<string></string>
-				<string>ar</string>
-				<string>en</string>
-				<string>fr</string>
-				<string>he</string>
-				<string>hi</string>
-			</array>
-			<key>Titles</key>
-			<array>
-				<string>Default</string>
-				<string>Arabic</string>
-				<string>English</string>
-				<string>French</string>
-				<string>Hebrew</string>
-				<string>Hindi</string>
-			</array>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSGroupSpecifier</string>
-			<key>Title</key>
-			<string>Debug Settings</string>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSToggleSwitchSpecifier</string>
-			<key>Title</key>
-			<string>Enable Startup Crash</string>
-			<key>Key</key>
-			<string>EnableStartupCrash</string>
-			<key>DefaultValue</key>
-			<false/>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSToggleSwitchSpecifier</string>
-			<key>Title</key>
-			<string>Enable Memory Debugging</string>
-			<key>Key</key>
-			<string>EnableMemoryDebugging</string>
-			<key>DefaultValue</key>
-			<false/>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSToggleSwitchSpecifier</string>
-			<key>Title</key>
-			<string>Use Mobile Safari UA</string>
-			<key>Key</key>
-			<string>UseMobileSafariUA</string>
-			<key>DefaultValue</key>
-			<false/>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSGroupSpecifier</string>
-			<key>Title</key>
-			<string>Google App Ecosystem</string>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSMultiValueSpecifier</string>
-			<key>Title</key>
-			<string>Gaia Environment</string>
-			<key>Key</key>
-			<string>GAIAEnvironment</string>
-			<key>DefaultValue</key>
-			<string></string>
-			<key>Values</key>
-			<array>
-				<string></string>
-				<string>Staging</string>
-				<string>Test</string>
-			</array>
-			<key>Titles</key>
-			<array>
-				<string>Prod</string>
-				<string>Staging</string>
-				<string>Test</string>
-			</array>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSToggleSwitchSpecifier</string>
-			<key>Title</key>
-			<string>Clear Application Group sandbox</string>
-			<key>Key</key>
-			<string>ClearApplicationGroup</string>
-			<key>DefaultValue</key>
-			<false/>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSTextFieldSpecifier</string>
-			<key>Title</key>
-			<string>Origin Server Host</string>
-			<key>Key</key>
-			<string>AlternateOriginServerHost</string>
-			<key>DefaultValue</key>
-			<string></string>
-			<key>AutocorrectionType</key>
-			<string>No</string>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSGroupSpecifier</string>
-			<key>Title</key>
-			<string>Extra Flags (one per line)</string>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSToggleSwitchSpecifier</string>
-			<key>Title</key>
-			<string>Append Extra Flags</string>
-			<key>Key</key>
-			<string>EnableFreeformCommandLineFlags</string>
-			<key>DefaultValue</key>
-			<false/>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSTextFieldSpecifier</string>
-			<key>Title</key>
-			<string>Flag1</string>
-			<key>Key</key>
-			<string>FreeformCommandLineFlag1</string>
-			<key>DefaultValue</key>
-			<string></string>
-			<key>AutocorrectionType</key>
-			<string>No</string>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSTextFieldSpecifier</string>
-			<key>Title</key>
-			<string>Flag2</string>
-			<key>Key</key>
-			<string>FreeformCommandLineFlag2</string>
-			<key>DefaultValue</key>
-			<string></string>
-			<key>AutocorrectionType</key>
-			<string>No</string>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSTextFieldSpecifier</string>
-			<key>Title</key>
-			<string>Flag3</string>
-			<key>Key</key>
-			<string>FreeformCommandLineFlag3</string>
-			<key>DefaultValue</key>
-			<string></string>
-			<key>AutocorrectionType</key>
-			<string>No</string>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSTextFieldSpecifier</string>
-			<key>Title</key>
-			<string>Flag4</string>
-			<key>Key</key>
-			<string>FreeformCommandLineFlag4</string>
-			<key>DefaultValue</key>
-			<string></string>
-			<key>AutocorrectionType</key>
-			<string>No</string>
-		</dict>
-		<dict>
-			<key>Type</key>
-			<string>PSTextFieldSpecifier</string>
-			<key>Title</key>
-			<string>Flag5</string>
-			<key>Key</key>
-			<string>FreeformCommandLineFlag5</string>
-			<key>DefaultValue</key>
-			<string></string>
-			<key>AutocorrectionType</key>
-			<string>No</string>
-		</dict>
 	</array>
 </dict>
 </plist>
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn
index 0bd4dcc..f524d082 100644
--- a/ios/chrome/browser/ui/authentication/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -46,6 +46,7 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/infobars",
+    "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/sync",
     "//ios/chrome/browser/tabs",
@@ -121,6 +122,7 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/infobars",
+    "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/prefs:browser_prefs",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/signin:test_support",
diff --git a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm
index 180e261..c527b57 100644
--- a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm
@@ -8,6 +8,7 @@
 #include "components/signin/public/identity_manager/identity_manager.h"
 #import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/metrics/previous_session_info.h"
 #include "ios/chrome/browser/signin/authentication_service.h"
 #include "ios/chrome/browser/signin/authentication_service_factory.h"
 #include "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h"
@@ -202,6 +203,22 @@
   if (!browserState || browserState->IsOffTheRecord()) {
     return NO;
   }
+
+  PreviousSessionInfo* prevSessionInfo = [PreviousSessionInfo sharedInstance];
+  if (prevSessionInfo.isFirstSessionAfterUpgrade &&
+      [prevSessionInfo.previousSessionVersion hasPrefix:@"77."]) {
+    // In M77, showing the signed-in account view was disabled due to the fact
+    // that the preferences used to compute authService->HaveAccountsChanged()
+    // were not correctly updated (see crbug.com/1006717).
+    // To avoid user confusion, it is important to avoid showing the signed-in
+    // accounts dialog on the first session after an update from M77 in order
+    // to allow the authentication service to update its internal preferences.
+    //
+    // TODO(crbug.com/1007990) Remove this code after M81 (revert
+    // https://chromium-review.googlesource.com/c/chromium/src/+/1824259 ).
+    return NO;
+  }
+
   AuthenticationService* authService =
       AuthenticationServiceFactory::GetForBrowserState(browserState);
   return !gSignedInAccountsViewControllerIsShown &&
diff --git a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm
index 448a1fc..b46bb33 100644
--- a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm
@@ -9,6 +9,8 @@
 #include "base/bind.h"
 #include "base/strings/sys_string_conversions.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/metrics/previous_session_info.h"
+#include "ios/chrome/browser/metrics/previous_session_info_private.h"
 #include "ios/chrome/browser/signin/authentication_service_factory.h"
 #import "ios/chrome/browser/signin/authentication_service_fake.h"
 #include "ios/chrome/test/block_cleanup_test.h"
@@ -65,3 +67,45 @@
   EXPECT_TRUE([SignedInAccountsViewController
       shouldBePresentedForBrowserState:browser_state_.get()]);
 }
+
+// Tests that the signed in accounts view shouldn't be presented on the first
+// session after upgrade.
+TEST_F(SignedInAccountsViewControllerTest,
+       ShouldBePresentedForBrowserStateAfterUpgrade) {
+  auth_service_->SetHaveAccountsChanged(true);
+
+  {
+    [PreviousSessionInfo resetSharedInstanceForTesting];
+    PreviousSessionInfo* prevSessionInfo = [PreviousSessionInfo sharedInstance];
+    [prevSessionInfo setIsFirstSessionAfterUpgrade:YES];
+    [prevSessionInfo setPreviousSessionVersion:nil];
+    EXPECT_TRUE([SignedInAccountsViewController
+        shouldBePresentedForBrowserState:browser_state_.get()]);
+  }
+
+  {
+    [PreviousSessionInfo resetSharedInstanceForTesting];
+    PreviousSessionInfo* prevSessionInfo = [PreviousSessionInfo sharedInstance];
+    [prevSessionInfo setIsFirstSessionAfterUpgrade:YES];
+    EXPECT_TRUE([SignedInAccountsViewController
+        shouldBePresentedForBrowserState:browser_state_.get()]);
+  }
+
+  {
+    [PreviousSessionInfo resetSharedInstanceForTesting];
+    PreviousSessionInfo* prevSessionInfo = [PreviousSessionInfo sharedInstance];
+    [prevSessionInfo setIsFirstSessionAfterUpgrade:YES];
+    [prevSessionInfo setPreviousSessionVersion:@"77.0.1.0"];
+    EXPECT_FALSE([SignedInAccountsViewController
+        shouldBePresentedForBrowserState:browser_state_.get()]);
+  }
+
+  {
+    [PreviousSessionInfo resetSharedInstanceForTesting];
+    PreviousSessionInfo* prevSessionInfo = [PreviousSessionInfo sharedInstance];
+    [prevSessionInfo setIsFirstSessionAfterUpgrade:YES];
+    [prevSessionInfo setPreviousSessionVersion:@"78.0.1.0"];
+    EXPECT_TRUE([SignedInAccountsViewController
+        shouldBePresentedForBrowserState:browser_state_.get()]);
+  }
+}
diff --git a/ios/chrome/browser/ui/util/BUILD.gn b/ios/chrome/browser/ui/util/BUILD.gn
index 3afba1b7..7fe7d1d 100644
--- a/ios/chrome/browser/ui/util/BUILD.gn
+++ b/ios/chrome/browser/ui/util/BUILD.gn
@@ -2,6 +2,29 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/buildflag_header.gni")
+import("//ios/build/chrome_build.gni")
+
+buildflag_header("ios_multi_window_buildflags") {
+  header = "multi_window_buildflags.h"
+  header_dir = "ios/chrome/browser/ui/util"
+  flags = [ "IOS_MULTIWINDOW_ENABLED=$ios_enable_multi_window" ]
+}
+
+source_set("multiwindow_util") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  sources = [
+    "multi_window_support.h",
+    "multi_window_support.mm",
+  ]
+
+  deps = [
+    ":ios_multi_window_buildflags",
+    "//base",
+  ]
+}
+
 source_set("util") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
diff --git a/ios/chrome/browser/ui/util/multi_window_support.h b/ios/chrome/browser/ui/util/multi_window_support.h
new file mode 100644
index 0000000..2513e4a
--- /dev/null
+++ b/ios/chrome/browser/ui/util/multi_window_support.h
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_UTIL_MULTI_WINDOW_SUPPORT_H_
+#define IOS_CHROME_BROWSER_UI_UTIL_MULTI_WINDOW_SUPPORT_H_
+
+// Returns true if multiwindow is supported on this OS version and is enabled in
+// the current build configuration. Does not check if this device can actually
+// show multiple windows (e.g. on iPhone): use [UIApplication
+// supportsMultipleScenes] instead.
+bool IsMultiwindowSupported();
+
+#endif  // IOS_CHROME_BROWSER_UI_UTIL_MULTI_WINDOW_SUPPORT_H_
diff --git a/ios/chrome/browser/ui/util/multi_window_support.mm b/ios/chrome/browser/ui/util/multi_window_support.mm
new file mode 100644
index 0000000..d75966a
--- /dev/null
+++ b/ios/chrome/browser/ui/util/multi_window_support.mm
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/util/multi_window_support.h"
+
+#include "base/ios/ios_util.h"
+#include "ios/chrome/browser/ui/util/multi_window_buildflags.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+bool IsMultiwindowSupported() {
+  return BUILDFLAG(IOS_MULTIWINDOW_ENABLED) &&
+         base::ios::IsRunningOnIOS13OrLater();
+}
diff --git a/ipc/ipc_channel.h b/ipc/ipc_channel.h
index 3a5abf02..04395f0 100644
--- a/ipc/ipc_channel.h
+++ b/ipc/ipc_channel.h
@@ -110,6 +110,8 @@
         const std::string& name,
         mojo::ScopedInterfaceEndpointHandle handle) = 0;
 
+    // Remove this after done with migrating all AsscoiatedInterfacePtr to
+    // AsscoiatedRemote.
     // Template helper to add an interface factory to this channel.
     template <typename Interface>
     using AssociatedInterfaceFactory =
@@ -122,6 +124,18 @@
           base::Bind(&BindAssociatedInterfaceRequest<Interface>, factory));
     }
 
+    // Template helper to add an interface factory to this channel.
+    template <typename Interface>
+    using AssociatedReceiverFactory =
+        base::Callback<void(mojo::PendingAssociatedReceiver<Interface>)>;
+    template <typename Interface>
+    void AddAssociatedInterface(
+        const AssociatedReceiverFactory<Interface>& factory) {
+      AddGenericAssociatedInterface(
+          Interface::Name_,
+          base::Bind(&BindPendingAssociatedReceiver<Interface>, factory));
+    }
+
     // Remove this after done with migrating all AsscoiatedInterfacePtr to
     // AsscoiatedRemote.
     // Template helper to request a remote associated interface.
@@ -142,6 +156,8 @@
     }
 
    private:
+    // Remove this after done with migrating all AsscoiatedInterfacePtr to
+    // AsscoiatedRemote.
     template <typename Interface>
     static void BindAssociatedInterfaceRequest(
         const AssociatedInterfaceFactory<Interface>& factory,
@@ -149,6 +165,14 @@
       factory.Run(
           mojo::AssociatedInterfaceRequest<Interface>(std::move(handle)));
     }
+
+    template <typename Interface>
+    static void BindPendingAssociatedReceiver(
+        const AssociatedReceiverFactory<Interface>& factory,
+        mojo::ScopedInterfaceEndpointHandle handle) {
+      factory.Run(
+          mojo::PendingAssociatedReceiver<Interface>(std::move(handle)));
+    }
   };
 
   // The maximum message size in bytes. Attempting to receive a message of this
diff --git a/ipc/ipc_channel_mojo_unittest.cc b/ipc/ipc_channel_mojo_unittest.cc
index 05ef81e..5ba72fb 100644
--- a/ipc/ipc_channel_mojo_unittest.cc
+++ b/ipc/ipc_channel_mojo_unittest.cc
@@ -48,7 +48,10 @@
 #include "ipc/ipc_test_base.h"
 #include "ipc/ipc_test_channel_listener.h"
 #include "mojo/core/embedder/embedder.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/system/wait.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -661,7 +664,7 @@
   static const int kNumMessages;
 
   explicit ListenerWithSimpleAssociatedInterface(base::OnceClosure quit_closure)
-      : quit_closure_(std::move(quit_closure)), binding_(this) {}
+      : quit_closure_(std::move(quit_closure)) {}
 
   ~ListenerWithSimpleAssociatedInterface() override = default;
 
@@ -678,8 +681,9 @@
 
   void RegisterInterfaceFactory(IPC::Channel* channel) {
     channel->GetAssociatedInterfaceSupport()->AddAssociatedInterface(
-        base::BindRepeating(&ListenerWithSimpleAssociatedInterface::BindRequest,
-                            base::Unretained(this)));
+        base::BindRepeating(
+            &ListenerWithSimpleAssociatedInterface::BindReceiver,
+            base::Unretained(this)));
   }
 
  private:
@@ -700,16 +704,17 @@
     std::move(quit_closure_).Run();
   }
 
-  void BindRequest(IPC::mojom::SimpleTestDriverAssociatedRequest request) {
-    DCHECK(!binding_.is_bound());
-    binding_.Bind(std::move(request));
+  void BindReceiver(
+      mojo::PendingAssociatedReceiver<IPC::mojom::SimpleTestDriver> receiver) {
+    DCHECK(!receiver_.is_bound());
+    receiver_.Bind(std::move(receiver));
   }
 
   int32_t next_expected_value_ = 0;
   int num_messages_received_ = 0;
   base::OnceClosure quit_closure_;
 
-  mojo::AssociatedBinding<IPC::mojom::SimpleTestDriver> binding_;
+  mojo::AssociatedReceiver<IPC::mojom::SimpleTestDriver> receiver_{this};
 };
 
 const int ListenerWithSimpleAssociatedInterface::kNumMessages = 1000;
@@ -724,7 +729,7 @@
   void OnChannelConnected(int32_t peer_pid) override {
     DCHECK(channel_);
     channel_->GetAssociatedInterfaceSupport()->GetRemoteAssociatedInterface(
-        &driver_);
+        driver_.BindNewEndpointAndPassReceiver());
 
     // Send a bunch of interleaved messages, alternating between the associated
     // interface and a legacy IPC::Message.
@@ -743,7 +748,7 @@
   void OnQuitAck() { std::move(quit_closure_).Run(); }
 
   IPC::Channel* channel_ = nullptr;
-  IPC::mojom::SimpleTestDriverAssociatedPtr driver_;
+  mojo::AssociatedRemote<IPC::mojom::SimpleTestDriver> driver_;
   base::OnceClosure quit_closure_;
 };
 
@@ -850,7 +855,7 @@
 
   explicit ListenerWithSimpleProxyAssociatedInterface(
       base::OnceClosure quit_closure)
-      : quit_closure_(std::move(quit_closure)), binding_(this) {}
+      : quit_closure_(std::move(quit_closure)) {}
 
   ~ListenerWithSimpleProxyAssociatedInterface() override = default;
 
@@ -869,8 +874,9 @@
       const std::string& interface_name,
       mojo::ScopedInterfaceEndpointHandle handle) override {
     DCHECK_EQ(interface_name, IPC::mojom::SimpleTestDriver::Name_);
-    binding_.Bind(
-        IPC::mojom::SimpleTestDriverAssociatedRequest(std::move(handle)));
+    receiver_.Bind(
+        mojo::PendingAssociatedReceiver<IPC::mojom::SimpleTestDriver>(
+            std::move(handle)));
   }
 
   bool received_all_messages() const {
@@ -891,20 +897,21 @@
 
   void RequestQuit(RequestQuitCallback callback) override {
     std::move(callback).Run();
-    binding_.Close();
+    receiver_.reset();
     std::move(quit_closure_).Run();
   }
 
-  void BindRequest(IPC::mojom::SimpleTestDriverAssociatedRequest request) {
-    DCHECK(!binding_.is_bound());
-    binding_.Bind(std::move(request));
+  void BindReceiver(
+      mojo::PendingAssociatedReceiver<IPC::mojom::SimpleTestDriver> receiver) {
+    DCHECK(!receiver_.is_bound());
+    receiver_.Bind(std::move(receiver));
   }
 
   int32_t next_expected_value_ = 0;
   int num_messages_received_ = 0;
   base::OnceClosure quit_closure_;
 
-  mojo::AssociatedBinding<IPC::mojom::SimpleTestDriver> binding_;
+  mojo::AssociatedReceiver<IPC::mojom::SimpleTestDriver> receiver_{this};
 };
 
 const int ListenerWithSimpleProxyAssociatedInterface::kNumMessages = 1000;
@@ -968,7 +975,7 @@
 
   // Send a bunch of interleaved messages, alternating between the associated
   // interface and a legacy IPC::Message.
-  IPC::mojom::SimpleTestDriverAssociatedPtr driver;
+  mojo::AssociatedRemote<IPC::mojom::SimpleTestDriver> driver;
   proxy()->GetRemoteAssociatedInterface(&driver);
   for (int i = 0; i < ListenerWithSimpleProxyAssociatedInterface::kNumMessages;
        ++i) {
@@ -1074,7 +1081,7 @@
     : public IPC::Listener,
       public IPC::mojom::SimpleTestDriver {
  public:
-  ListenerWithSyncAssociatedInterface() : binding_(this) {}
+  ListenerWithSyncAssociatedInterface() = default;
   ~ListenerWithSyncAssociatedInterface() override = default;
 
   void set_sync_sender(IPC::Sender* sync_sender) { sync_sender_ = sync_sender; }
@@ -1085,7 +1092,7 @@
     loop.Run();
   }
 
-  void CloseBinding() { binding_.Close(); }
+  void CloseBinding() { receiver_.reset(); }
 
   void set_response_value(int32_t response) {
     response_value_ = response;
@@ -1126,15 +1133,17 @@
   void OnAssociatedInterfaceRequest(
       const std::string& interface_name,
       mojo::ScopedInterfaceEndpointHandle handle) override {
-    DCHECK(!binding_.is_bound());
+    DCHECK(!receiver_.is_bound());
     DCHECK_EQ(interface_name, IPC::mojom::SimpleTestDriver::Name_);
-    binding_.Bind(
-        IPC::mojom::SimpleTestDriverAssociatedRequest(std::move(handle)));
+    receiver_.Bind(
+        mojo::PendingAssociatedReceiver<IPC::mojom::SimpleTestDriver>(
+            std::move(handle)));
   }
 
-  void BindRequest(IPC::mojom::SimpleTestDriverAssociatedRequest request) {
-    DCHECK(!binding_.is_bound());
-    binding_.Bind(std::move(request));
+  void BindReceiver(
+      mojo::PendingAssociatedReceiver<IPC::mojom::SimpleTestDriver> receiver) {
+    DCHECK(!receiver_.is_bound());
+    receiver_.Bind(std::move(receiver));
   }
 
   IPC::Sender* sync_sender_ = nullptr;
@@ -1142,7 +1151,7 @@
   int32_t response_value_ = 0;
   base::OnceClosure quit_closure_;
 
-  mojo::AssociatedBinding<IPC::mojom::SimpleTestDriver> binding_;
+  mojo::AssociatedReceiver<IPC::mojom::SimpleTestDriver> receiver_{this};
 };
 
 class SyncReplyReader : public IPC::MessageReplyDeserializer {
@@ -1178,7 +1187,7 @@
   // Verify that we can send a sync IPC and service an incoming sync request
   // while waiting on it
   listener.set_response_value(42);
-  IPC::mojom::SimpleTestClientAssociatedPtr client;
+  mojo::AssociatedRemote<IPC::mojom::SimpleTestClient> client;
   proxy()->GetRemoteAssociatedInterface(&client);
   int32_t received_value;
   EXPECT_TRUE(client->RequestValue(&received_value));
@@ -1208,7 +1217,8 @@
 class SimpleTestClientImpl : public IPC::mojom::SimpleTestClient,
                              public IPC::Listener {
  public:
-  SimpleTestClientImpl() : binding_(this) {}
+  SimpleTestClientImpl() = default;
+  ~SimpleTestClientImpl() override = default;
 
   void set_driver(IPC::mojom::SimpleTestDriver* driver) { driver_ = driver; }
   void set_sync_sender(IPC::Sender* sync_sender) { sync_sender_ = sync_sender; }
@@ -1259,15 +1269,15 @@
   void OnAssociatedInterfaceRequest(
       const std::string& interface_name,
       mojo::ScopedInterfaceEndpointHandle handle) override {
-    DCHECK(!binding_.is_bound());
+    DCHECK(!receiver_.is_bound());
     DCHECK_EQ(interface_name, IPC::mojom::SimpleTestClient::Name_);
 
-    binding_.Bind(
+    receiver_.Bind(
         IPC::mojom::SimpleTestClientAssociatedRequest(std::move(handle)));
   }
 
   bool use_sync_sender_ = false;
-  mojo::AssociatedBinding<IPC::mojom::SimpleTestClient> binding_;
+  mojo::AssociatedBinding<IPC::mojom::SimpleTestClient> receiver_{this};
   IPC::Sender* sync_sender_ = nullptr;
   IPC::mojom::SimpleTestDriver* driver_ = nullptr;
   std::unique_ptr<base::RunLoop> run_loop_;
@@ -1282,7 +1292,7 @@
   client_impl.set_sync_sender(proxy());
   RunProxy();
 
-  IPC::mojom::SimpleTestDriverAssociatedPtr driver;
+  mojo::AssociatedRemote<IPC::mojom::SimpleTestDriver> driver;
   proxy()->GetRemoteAssociatedInterface(&driver);
   client_impl.set_driver(driver.get());
 
@@ -1416,12 +1426,13 @@
 
   IPC::mojom::AssociatedInterfaceVendorAssociatedPtr vendor;
   proxy()->GetRemoteAssociatedInterface(&vendor);
-  IPC::mojom::SimpleTestDriverAssociatedPtr tester;
-  vendor->GetTestInterface(mojo::MakeRequest(&tester));
+  mojo::AssociatedRemote<IPC::mojom::SimpleTestDriver> tester;
+  vendor->GetTestInterface(tester.BindNewEndpointAndPassReceiver());
   base::RunLoop run_loop;
-  tester.set_connection_error_handler(run_loop.QuitClosure());
+  tester.set_disconnect_handler(run_loop.QuitClosure());
   run_loop.Run();
 
+  tester.reset();
   proxy()->GetRemoteAssociatedInterface(&tester);
   EXPECT_TRUE(WaitForClientShutdown());
   DestroyProxy();
diff --git a/ipc/ipc_test.mojom b/ipc/ipc_test.mojom
index fc24e37..9276df0 100644
--- a/ipc/ipc_test.mojom
+++ b/ipc/ipc_test.mojom
@@ -43,7 +43,7 @@
 };
 
 interface AssociatedInterfaceVendor {
-  GetTestInterface(associated SimpleTestDriver& interface_reqest);
+  GetTestInterface(pending_associated_receiver<SimpleTestDriver> receiver);
 };
 
 interface InterfacePassingTestDriver {
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index f1655d20..8528a92 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -355,7 +355,7 @@
 #if defined(ARCH_CPU_X86_FAMILY) && defined(OS_CHROMEOS)
 // Enable VP9 k-SVC decoding with HW decoder for webrtc use case on ChromeOS.
 const base::Feature kVp9kSVCHWDecoding{"Vp9kSVCHWDecoding",
-                                       base::FEATURE_DISABLED_BY_DEFAULT};
+                                       base::FEATURE_ENABLED_BY_DEFAULT};
 #endif  //  defined(ARCH_CPU_X86_FAMILY) && defined(OS_CHROMEOS)
 
 // Inform video blitter of video color space.
diff --git a/media/capture/video/chromeos/camera_device_context.cc b/media/capture/video/chromeos/camera_device_context.cc
index cb5f91ae..03e98623 100644
--- a/media/capture/video/chromeos/camera_device_context.cc
+++ b/media/capture/video/chromeos/camera_device_context.cc
@@ -4,25 +4,6 @@
 
 #include "media/capture/video/chromeos/camera_device_context.h"
 
-namespace {
-media::VideoRotation CameraFrameOrientationToVideoRotation(
-    int frame_orientation) {
-  switch (frame_orientation) {
-    case 0:
-      return media::VideoRotation::VIDEO_ROTATION_0;
-    case 90:
-      return media::VideoRotation::VIDEO_ROTATION_90;
-    case 180:
-      return media::VideoRotation::VIDEO_ROTATION_180;
-    case 270:
-      return media::VideoRotation::VIDEO_ROTATION_270;
-    default:
-      NOTREACHED() << "Invalid frame orientation " << frame_orientation;
-      return media::VideoRotation::VIDEO_ROTATION_0;
-  }
-}
-}  // namespace
-
 namespace media {
 
 CameraDeviceContext::CameraDeviceContext(
@@ -68,9 +49,9 @@
     base::TimeTicks reference_time,
     base::TimeDelta timestamp) {
   VideoFrameMetadata metadata;
-  metadata.SetRotation(
-      VideoFrameMetadata::Key::ROTATION,
-      CameraFrameOrientationToVideoRotation(GetCameraFrameOrientation()));
+  // All frames are pre-rotated to the display orientation.
+  metadata.SetRotation(VideoFrameMetadata::Key::ROTATION,
+                       VideoRotation::VIDEO_ROTATION_0);
   // TODO: Figure out the right color space for the camera frame.  We may need
   // to populate the camera metadata with the color space reported by the V4L2
   // device.
diff --git a/media/capture/video/chromeos/request_manager.cc b/media/capture/video/chromeos/request_manager.cc
index 8579e40f..b22398d 100644
--- a/media/capture/video/chromeos/request_manager.cc
+++ b/media/capture/video/chromeos/request_manager.cc
@@ -820,15 +820,15 @@
                                                  uint64_t buffer_ipc_id) {
   const CaptureResult& pending_result = pending_results_[frame_number];
   if (video_capture_use_gmb_) {
+    VideoCaptureFormat format;
     base::Optional<VideoCaptureDevice::Client::Buffer> buffer =
         stream_buffer_manager_->AcquireBufferForClientById(
-            StreamType::kPreviewOutput, buffer_ipc_id);
+            StreamType::kPreviewOutput, buffer_ipc_id,
+            device_context_->GetCameraFrameOrientation(), &format);
     CHECK(buffer);
     device_context_->SubmitCapturedVideoCaptureBuffer(
-        std::move(*buffer),
-        stream_buffer_manager_->GetStreamCaptureFormat(
-            StreamType::kPreviewOutput),
-        pending_result.reference_time, pending_result.timestamp);
+        std::move(*buffer), format, pending_result.reference_time,
+        pending_result.timestamp);
     // |buffer| ownership is transferred to client, so we need to reserve a
     // new video buffer.
     stream_buffer_manager_->ReserveBuffer(StreamType::kPreviewOutput);
diff --git a/media/capture/video/chromeos/stream_buffer_manager.cc b/media/capture/video/chromeos/stream_buffer_manager.cc
index 261c335..2ade0321 100644
--- a/media/capture/video/chromeos/stream_buffer_manager.cc
+++ b/media/capture/video/chromeos/stream_buffer_manager.cc
@@ -20,6 +20,7 @@
 #include "media/capture/video/chromeos/request_builder.h"
 #include "mojo/public/cpp/platform/platform_handle.h"
 #include "mojo/public/cpp/system/platform_handle.h"
+#include "third_party/libyuv/include/libyuv.h"
 
 namespace media {
 
@@ -62,7 +63,9 @@
 
 base::Optional<StreamBufferManager::Buffer>
 StreamBufferManager::AcquireBufferForClientById(StreamType stream_type,
-                                                uint64_t buffer_ipc_id) {
+                                                uint64_t buffer_ipc_id,
+                                                int rotation,
+                                                VideoCaptureFormat* format) {
   DCHECK(stream_context_.count(stream_type));
   auto& stream_context = stream_context_[stream_type];
   auto it = stream_context->buffers.find(GetBufferKey(buffer_ipc_id));
@@ -73,7 +76,84 @@
   }
   auto buffer_pair = std::move(it->second);
   stream_context->buffers.erase(it);
-  return std::move(buffer_pair.vcd_buffer);
+  *format = GetStreamCaptureFormat(stream_type);
+  // We only support NV12 at the moment.
+  DCHECK_EQ(format->pixel_format, PIXEL_FORMAT_NV12);
+
+  if (rotation == 0) {
+    return std::move(buffer_pair.vcd_buffer);
+  }
+
+  if (rotation == 90 || rotation == 270) {
+    format->frame_size =
+        gfx::Size(format->frame_size.height(), format->frame_size.width());
+  }
+  Buffer rotated_buffer;
+  if (!device_context_->ReserveVideoCaptureBufferFromPool(
+          format->frame_size, format->pixel_format, &rotated_buffer)) {
+    DLOG(WARNING) << "Failed to reserve video capture buffer";
+    return std::move(buffer_pair.vcd_buffer);
+  }
+
+  base::Optional<gfx::BufferFormat> gfx_format =
+      PixFormatVideoToGfx(format->pixel_format);
+  DCHECK(gfx_format);
+  auto rotated_gmb = gmb_support_->CreateGpuMemoryBufferImplFromHandle(
+      rotated_buffer.handle_provider->GetGpuMemoryBufferHandle(),
+      format->frame_size, *gfx_format,
+      CameraBufferFactory::GetBufferUsage(*gfx_format), base::NullCallback());
+
+  if (!rotated_gmb || !rotated_gmb->Map()) {
+    DLOG(WARNING) << "Failed to map rotated buffer";
+    return std::move(buffer_pair.vcd_buffer);
+  }
+  const auto& original_gmb = buffer_pair.gmb;
+  if (!original_gmb->Map()) {
+    DLOG(WARNING) << "Failed to map original buffer";
+    rotated_gmb->Unmap();
+    return std::move(buffer_pair.vcd_buffer);
+  }
+
+  const size_t original_width = stream_context->buffer_dimension.width();
+  const size_t original_height = stream_context->buffer_dimension.height();
+  const size_t temp_buffer_size =
+      ((original_width + 1) & ~1) * ((original_height + 1) & ~1) / 2;
+  std::vector<uint8_t> temp_uv_buffer(temp_buffer_size);
+  uint8_t* temp_u = temp_uv_buffer.data();
+  uint8_t* temp_v = temp_uv_buffer.data() + (temp_buffer_size / 2);
+
+  // libyuv currently provides only NV12ToI420Rotate. We achieve NV12 rotation
+  // by NV12ToI420Rotate then merge the I420 U and V planes into the final NV12
+  // UV plane.
+  auto translate_rotation = [](const int rotation) -> libyuv::RotationModeEnum {
+    switch (rotation) {
+      case 0:
+        return libyuv::kRotate0;
+      case 90:
+        return libyuv::kRotate90;
+      case 180:
+        return libyuv::kRotate180;
+      case 270:
+        return libyuv::kRotate270;
+    }
+    return libyuv::kRotate0;
+  };
+  const size_t temp_chroma_width = (rotated_gmb->GetSize().width() + 1) / 2;
+  const size_t temp_chroma_height = (rotated_gmb->GetSize().height() + 1) / 2;
+  libyuv::NV12ToI420Rotate(
+      static_cast<uint8_t*>(original_gmb->memory(0)), original_gmb->stride(0),
+      static_cast<uint8_t*>(original_gmb->memory(1)), original_gmb->stride(1),
+      static_cast<uint8_t*>(rotated_gmb->memory(0)), rotated_gmb->stride(0),
+      temp_u, temp_chroma_width, temp_v, temp_chroma_width, original_width,
+      original_height, translate_rotation(rotation));
+  libyuv::MergeUVPlane(temp_u, temp_chroma_width, temp_v, temp_chroma_width,
+                       static_cast<uint8_t*>(rotated_gmb->memory(1)),
+                       rotated_gmb->stride(1), temp_chroma_width,
+                       temp_chroma_height);
+
+  rotated_gmb->Unmap();
+  original_gmb->Unmap();
+  return std::move(rotated_buffer);
 }
 
 VideoCaptureFormat StreamBufferManager::GetStreamCaptureFormat(
diff --git a/media/capture/video/chromeos/stream_buffer_manager.h b/media/capture/video/chromeos/stream_buffer_manager.h
index c940509..cd231498 100644
--- a/media/capture/video/chromeos/stream_buffer_manager.h
+++ b/media/capture/video/chromeos/stream_buffer_manager.h
@@ -59,8 +59,16 @@
 
   gfx::GpuMemoryBuffer* GetGpuMemoryBufferById(StreamType stream_type,
                                                uint64_t buffer_ipc_id);
+
+  // Acquires the VCD client buffer specified by |stream_type| and
+  // |buffer_ipc_id|, with optional rotation applied.  |rotation| is the
+  // clockwise degrees that the source frame would be rotated to, and the valid
+  // values are 0, 90, 180, and 270.  Returns the VideoCaptureFormat of the
+  // returned buffer in |format|.
   base::Optional<Buffer> AcquireBufferForClientById(StreamType stream_type,
-                                                    uint64_t buffer_ipc_id);
+                                                    uint64_t buffer_ipc_id,
+                                                    int rotation,
+                                                    VideoCaptureFormat* format);
 
   VideoCaptureFormat GetStreamCaptureFormat(StreamType stream_type);
 
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 041f6054..a9e3996b 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -330,6 +330,10 @@
     if (use_v4l2_codec) {
       deps += [ "//media/gpu/v4l2" ]
     }
+
+    if (use_vaapi) {
+      deps += [ "//media/gpu/vaapi" ]
+    }
   }
 
   source_set("image_processor_common") {
diff --git a/media/gpu/image_processor.cc b/media/gpu/image_processor.cc
index 4a7e6857..7908801 100644
--- a/media/gpu/image_processor.cc
+++ b/media/gpu/image_processor.cc
@@ -47,6 +47,12 @@
 
   return ProcessInternal(std::move(frame), BindToCurrentLoop(std::move(cb)));
 }
+
+bool ImageProcessor::ProcessInternal(scoped_refptr<VideoFrame> frame,
+                                     LegacyFrameReadyCB cb) {
+  NOTIMPLEMENTED();
+  return false;
+}
 #endif
 
 bool ImageProcessor::Process(scoped_refptr<VideoFrame> input_frame,
diff --git a/media/gpu/image_processor.h b/media/gpu/image_processor.h
index a2a71782..771652a 100644
--- a/media/gpu/image_processor.h
+++ b/media/gpu/image_processor.h
@@ -173,7 +173,7 @@
   // "client thread".
 #if defined(OS_POSIX) || defined(OS_FUCHSIA)
   virtual bool ProcessInternal(scoped_refptr<VideoFrame> frame,
-                               LegacyFrameReadyCB cb) = 0;
+                               LegacyFrameReadyCB cb);
 #endif
   virtual bool ProcessInternal(scoped_refptr<VideoFrame> input_frame,
                                scoped_refptr<VideoFrame> output_frame,
diff --git a/media/gpu/image_processor_factory.cc b/media/gpu/image_processor_factory.cc
index ba259c2..e00dd905 100644
--- a/media/gpu/image_processor_factory.cc
+++ b/media/gpu/image_processor_factory.cc
@@ -10,6 +10,10 @@
 #include "media/gpu/buildflags.h"
 #include "media/gpu/libyuv_image_processor.h"
 
+#if BUILDFLAG(USE_VAAPI)
+#include "media/gpu/vaapi/vaapi_image_processor.h"
+#endif  // BUILDFLAG(USE_VAAPI)
+
 #if BUILDFLAG(USE_V4L2_CODEC)
 #include "media/gpu/v4l2/v4l2_device.h"
 #include "media/gpu/v4l2/v4l2_image_processor.h"
@@ -25,6 +29,12 @@
     size_t num_buffers,
     ImageProcessor::ErrorCB error_cb) {
   std::unique_ptr<ImageProcessor> image_processor;
+#if BUILDFLAG(USE_VAAPI)
+  image_processor = VaapiImageProcessor::Create(
+      input_config, output_config, preferred_output_modes, error_cb);
+  if (image_processor)
+    return image_processor;
+#endif  // BUILDFLAG(USE_VAAPI)
 #if BUILDFLAG(USE_V4L2_CODEC)
   for (auto output_mode : preferred_output_modes) {
     image_processor = V4L2ImageProcessor::Create(
diff --git a/media/gpu/libyuv_image_processor.cc b/media/gpu/libyuv_image_processor.cc
index 2f86597..68d148ec 100644
--- a/media/gpu/libyuv_image_processor.cc
+++ b/media/gpu/libyuv_image_processor.cc
@@ -164,16 +164,6 @@
   return processor;
 }
 
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-bool LibYUVImageProcessor::ProcessInternal(
-    scoped_refptr<VideoFrame> frame,
-    LegacyFrameReadyCB cb) {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
-  NOTIMPLEMENTED();
-  return false;
-}
-#endif
-
 bool LibYUVImageProcessor::ProcessInternal(
     scoped_refptr<VideoFrame> input_frame,
     scoped_refptr<VideoFrame> output_frame,
diff --git a/media/gpu/libyuv_image_processor.h b/media/gpu/libyuv_image_processor.h
index 5a86b7c6..aaf5131 100644
--- a/media/gpu/libyuv_image_processor.h
+++ b/media/gpu/libyuv_image_processor.h
@@ -59,10 +59,6 @@
                        ErrorCB error_cb);
 
   // ImageProcessor override
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-  bool ProcessInternal(scoped_refptr<VideoFrame> frame,
-                       LegacyFrameReadyCB cb) override;
-#endif
   bool ProcessInternal(scoped_refptr<VideoFrame> input_frame,
                        scoped_refptr<VideoFrame> output_frame,
                        FrameReadyCB cb) override;
diff --git a/media/gpu/linux/dmabuf_video_frame_pool.cc b/media/gpu/linux/dmabuf_video_frame_pool.cc
index 9ee770e..baeea7d 100644
--- a/media/gpu/linux/dmabuf_video_frame_pool.cc
+++ b/media/gpu/linux/dmabuf_video_frame_pool.cc
@@ -6,6 +6,12 @@
 
 namespace media {
 
+// static
+DmabufVideoFramePool::DmabufId DmabufVideoFramePool::GetDmabufId(
+    const VideoFrame& frame) {
+  return &(frame.DmabufFds());
+}
+
 DmabufVideoFramePool::DmabufVideoFramePool() = default;
 
 DmabufVideoFramePool::~DmabufVideoFramePool() = default;
diff --git a/media/gpu/linux/dmabuf_video_frame_pool.h b/media/gpu/linux/dmabuf_video_frame_pool.h
index 9ecda9a..66426ad 100644
--- a/media/gpu/linux/dmabuf_video_frame_pool.h
+++ b/media/gpu/linux/dmabuf_video_frame_pool.h
@@ -23,6 +23,12 @@
 // implementation must be thread-safe.
 class MEDIA_GPU_EXPORT DmabufVideoFramePool {
  public:
+  using DmabufId = const std::vector<base::ScopedFD>*;
+
+  // Get the identifier of Dmabuf-backed |frame|. Calling this method with the
+  // frames backed by the same Dmabuf should return the same result.
+  static DmabufId GetDmabufId(const VideoFrame& frame);
+
   DmabufVideoFramePool();
   virtual ~DmabufVideoFramePool();
 
diff --git a/media/gpu/linux/platform_video_frame_pool.cc b/media/gpu/linux/platform_video_frame_pool.cc
index 55dffbf..9dde20e 100644
--- a/media/gpu/linux/platform_video_frame_pool.cc
+++ b/media/gpu/linux/platform_video_frame_pool.cc
@@ -27,12 +27,6 @@
                                   gfx::BufferUsage::SCANOUT_VDA_WRITE);
 }
 
-// Get the identifier of |frame|. Calling this method with the frames backed
-// by the same Dmabuf should return the same result.
-PlatformVideoFramePool::DmabufId GetDmabufId(const VideoFrame& frame) {
-  return &(frame.DmabufFds());
-}
-
 }  // namespace
 
 PlatformVideoFramePool::PlatformVideoFramePool()
diff --git a/media/gpu/linux/platform_video_frame_pool.h b/media/gpu/linux/platform_video_frame_pool.h
index dfdd1473..dea062c0 100644
--- a/media/gpu/linux/platform_video_frame_pool.h
+++ b/media/gpu/linux/platform_video_frame_pool.h
@@ -34,8 +34,6 @@
 // old parameter values will be purged from the pool.
 class MEDIA_GPU_EXPORT PlatformVideoFramePool : public DmabufVideoFramePool {
  public:
-  using DmabufId = const std::vector<base::ScopedFD>*;
-
   PlatformVideoFramePool();
   ~PlatformVideoFramePool() override;
 
diff --git a/media/gpu/linux/platform_video_frame_pool_unittest.cc b/media/gpu/linux/platform_video_frame_pool_unittest.cc
index 0e84b17..00d90fc 100644
--- a/media/gpu/linux/platform_video_frame_pool_unittest.cc
+++ b/media/gpu/linux/platform_video_frame_pool_unittest.cc
@@ -50,7 +50,7 @@
 class PlatformVideoFramePoolTest
     : public ::testing::TestWithParam<VideoPixelFormat> {
  public:
-  using DmabufId = PlatformVideoFramePool::DmabufId;
+  using DmabufId = DmabufVideoFramePool::DmabufId;
 
   PlatformVideoFramePoolTest()
       : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
@@ -86,8 +86,6 @@
     EXPECT_EQ(size, pool_->GetPoolSizeForTesting());
   }
 
-  DmabufId GetDmabufId(const VideoFrame& frame) { return &(frame.DmabufFds()); }
-
  protected:
   base::test::TaskEnvironment task_environment_;
   std::unique_ptr<PlatformVideoFramePool,
@@ -108,7 +106,7 @@
 TEST_F(PlatformVideoFramePoolTest, SingleFrameReuse) {
   SetFrameFormat(PIXEL_FORMAT_I420);
   scoped_refptr<VideoFrame> frame = GetFrame(10);
-  DmabufId id = GetDmabufId(*frame);
+  DmabufId id = DmabufVideoFramePool::GetDmabufId(*frame);
 
   // Clear frame reference to return the frame to the pool.
   frame = nullptr;
@@ -116,25 +114,25 @@
 
   // Verify that the next frame from the pool uses the same memory.
   scoped_refptr<VideoFrame> new_frame = GetFrame(20);
-  EXPECT_EQ(id, GetDmabufId(*new_frame));
+  EXPECT_EQ(id, DmabufVideoFramePool::GetDmabufId(*new_frame));
 }
 
 TEST_F(PlatformVideoFramePoolTest, MultipleFrameReuse) {
   SetFrameFormat(PIXEL_FORMAT_I420);
   scoped_refptr<VideoFrame> frame1 = GetFrame(10);
   scoped_refptr<VideoFrame> frame2 = GetFrame(20);
-  DmabufId id1 = GetDmabufId(*frame1);
-  DmabufId id2 = GetDmabufId(*frame2);
+  DmabufId id1 = DmabufVideoFramePool::GetDmabufId(*frame1);
+  DmabufId id2 = DmabufVideoFramePool::GetDmabufId(*frame2);
 
   frame1 = nullptr;
   task_environment_.RunUntilIdle();
   frame1 = GetFrame(30);
-  EXPECT_EQ(id1, GetDmabufId(*frame1));
+  EXPECT_EQ(id1, DmabufVideoFramePool::GetDmabufId(*frame1));
 
   frame2 = nullptr;
   task_environment_.RunUntilIdle();
   frame2 = GetFrame(40);
-  EXPECT_EQ(id2, GetDmabufId(*frame2));
+  EXPECT_EQ(id2, DmabufVideoFramePool::GetDmabufId(*frame2));
 
   frame1 = nullptr;
   frame2 = nullptr;
diff --git a/media/gpu/linux/platform_video_frame_utils.h b/media/gpu/linux/platform_video_frame_utils.h
index 046fe96..66e24c9 100644
--- a/media/gpu/linux/platform_video_frame_utils.h
+++ b/media/gpu/linux/platform_video_frame_utils.h
@@ -9,10 +9,10 @@
 #include "media/base/video_frame.h"
 #include "media/gpu/media_gpu_export.h"
 #include "ui/gfx/buffer_types.h"
+#include "ui/gfx/linux/native_pixmap_dmabuf.h"
 
 namespace gfx {
 struct GpuMemoryBufferHandle;
-class NativePixmapDmaBuf;
 }  // namespace gfx
 
 namespace media {
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn
index 4a3df4ff7..4e63c6e 100644
--- a/media/gpu/v4l2/BUILD.gn
+++ b/media/gpu/v4l2/BUILD.gn
@@ -33,6 +33,8 @@
     "v4l2_decode_surface_handler.h",
     "v4l2_device.cc",
     "v4l2_device.h",
+    "v4l2_device_poller.cc",
+    "v4l2_device_poller.h",
     "v4l2_h264_accelerator.cc",
     "v4l2_h264_accelerator.h",
     "v4l2_h264_accelerator_legacy.cc",
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index cd71126..6a6d6db 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -903,6 +903,8 @@
   auto inserted = queued_buffers_.emplace(v4l2_buffer->index);
   DCHECK_EQ(inserted.second, true);
 
+  device_->SchedulePoll();
+
   return true;
 }
 
@@ -946,6 +948,9 @@
   DCHECK(it != queued_buffers_.end());
   queued_buffers_.erase(*it);
 
+  if (QueuedBuffersCount() > 0)
+    device_->SchedulePoll();
+
   DCHECK(free_buffers_);
   return std::make_pair(true,
                         V4L2BufferRefFactory::CreateReadableRef(
@@ -1759,4 +1764,37 @@
   return profiles;
 }
 
+bool V4L2Device::StartPolling(V4L2DevicePoller::EventCallback event_callback,
+                              base::RepeatingClosure error_callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  if (!device_poller_) {
+    device_poller_ =
+        std::make_unique<V4L2DevicePoller>(this, "V4L2DeviceThreadPoller");
+  }
+
+  bool ret = device_poller_->StartPolling(std::move(event_callback),
+                                          std::move(error_callback));
+
+  if (!ret)
+    device_poller_ = nullptr;
+
+  return ret;
+}
+
+bool V4L2Device::StopPolling() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  return !device_poller_ || device_poller_->StopPolling();
+}
+
+void V4L2Device::SchedulePoll() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  if (!device_poller_ || !device_poller_->IsPolling())
+    return;
+
+  device_poller_->SchedulePoll();
+}
+
 }  //  namespace media
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h
index 4bd5701..3c6da09d 100644
--- a/media/gpu/v4l2/v4l2_device.h
+++ b/media/gpu/v4l2/v4l2_device.h
@@ -25,6 +25,7 @@
 #include "media/base/video_frame.h"
 #include "media/base/video_frame_layout.h"
 #include "media/gpu/media_gpu_export.h"
+#include "media/gpu/v4l2/v4l2_device_poller.h"
 #include "media/video/video_decode_accelerator.h"
 #include "media/video/video_encode_accelerator.h"
 #include "ui/gfx/geometry/size.h"
@@ -539,6 +540,19 @@
   virtual bool IsJpegDecodingSupported() = 0;
   virtual bool IsJpegEncodingSupported() = 0;
 
+  // Start polling on this V4L2Device. |event_callback| will be posted to
+  // the caller's sequence if a buffer is ready to be dequeued and/or a V4L2
+  // event has been posted. |error_callback| will be posted to the client's
+  // sequence if a polling error has occurred.
+  bool StartPolling(V4L2DevicePoller::EventCallback event_callback,
+                    base::RepeatingClosure error_callback);
+  // Stop polling this V4L2Device if polling was active. No new events will
+  // be posted after this method has returned.
+  bool StopPolling();
+  // Schedule a polling event if polling is enabled. This method is intended
+  // to be called from V4L2Queue, clients should not need to call it directly.
+  void SchedulePoll();
+
  protected:
   friend class base::RefCountedThreadSafe<V4L2Device>;
   V4L2Device();
@@ -563,6 +577,10 @@
   // in queues_.
   void OnQueueDestroyed(v4l2_buf_type buf_type);
 
+  // Used if EnablePolling() is called to signal the user that an event
+  // happened or a buffer is ready to be dequeued.
+  std::unique_ptr<V4L2DevicePoller> device_poller_;
+
   SEQUENCE_CHECKER(client_sequence_checker_);
 };
 
diff --git a/media/gpu/v4l2/v4l2_device_poller.cc b/media/gpu/v4l2/v4l2_device_poller.cc
new file mode 100644
index 0000000..3d3e5fff
--- /dev/null
+++ b/media/gpu/v4l2/v4l2_device_poller.cc
@@ -0,0 +1,122 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/v4l2/v4l2_device_poller.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_checker.h"
+#include "media/gpu/macros.h"
+#include "media/gpu/v4l2/v4l2_device.h"
+
+namespace media {
+
+V4L2DevicePoller::V4L2DevicePoller(V4L2Device* const device,
+                                   const std::string& thread_name)
+    : device_(device),
+      poll_thread_(std::move(thread_name)),
+      trigger_poll_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                    base::WaitableEvent::InitialState::NOT_SIGNALED),
+      stop_polling_(false) {
+  DETACH_FROM_SEQUENCE(client_sequence_checker_);
+}
+
+V4L2DevicePoller::~V4L2DevicePoller() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  StopPolling();
+}
+
+bool V4L2DevicePoller::StartPolling(EventCallback event_callback,
+                                    base::RepeatingClosure error_callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  if (IsPolling())
+    return true;
+
+  client_task_runner_ = base::SequencedTaskRunnerHandle::Get();
+  error_callback_ = error_callback;
+
+  if (!poll_thread_.Start()) {
+    VLOGF(1) << "Failed to start device poll thread";
+    return false;
+  }
+
+  event_callback_ = std::move(event_callback);
+
+  stop_polling_.store(false);
+  poll_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&V4L2DevicePoller::DevicePollTask,
+                                base::Unretained(this)));
+
+  SchedulePoll();
+
+  return true;
+}
+
+bool V4L2DevicePoller::StopPolling() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  if (!IsPolling())
+    return true;
+
+  stop_polling_.store(true);
+
+  trigger_poll_.Signal();
+
+  if (!device_->SetDevicePollInterrupt()) {
+    VLOGF(1) << "Failed to interrupt device poll.";
+    return false;
+  }
+
+  DVLOGF(3) << "Stop device poll thread";
+  poll_thread_.Stop();
+
+  if (!device_->ClearDevicePollInterrupt()) {
+    VLOGF(1) << "Failed to clear interrupting device poll.";
+    return false;
+  }
+
+  return true;
+}
+
+bool V4L2DevicePoller::IsPolling() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  return poll_thread_.IsRunning();
+}
+
+void V4L2DevicePoller::SchedulePoll() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+
+  // A call to DevicePollTask() will be posted when we actually start polling.
+  if (!IsPolling())
+    return;
+
+  trigger_poll_.Signal();
+}
+
+void V4L2DevicePoller::DevicePollTask() {
+  DCHECK(poll_thread_.task_runner()->RunsTasksInCurrentSequence());
+
+  while (true) {
+    trigger_poll_.Wait();
+
+    if (stop_polling_)
+      break;
+
+    bool event_pending = false;
+    if (!device_->Poll(true, &event_pending)) {
+      client_task_runner_->PostTask(FROM_HERE, error_callback_);
+      return;
+    }
+
+    client_task_runner_->PostTask(FROM_HERE,
+                                  base::Bind(event_callback_, event_pending));
+  }
+}
+
+}  // namespace media
diff --git a/media/gpu/v4l2/v4l2_device_poller.h b/media/gpu/v4l2/v4l2_device_poller.h
new file mode 100644
index 0000000..21cd3b2
--- /dev/null
+++ b/media/gpu/v4l2/v4l2_device_poller.h
@@ -0,0 +1,95 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_V4L2_V4L2_DEVICE_POLLER_H_
+#define MEDIA_GPU_V4L2_V4L2_DEVICE_POLLER_H_
+
+#include <atomic>
+#include "base/callback_forward.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+
+namespace media {
+
+class V4L2Device;
+
+// Allows a client to poll() on a given V4L2Device and be signaled when
+// a buffer is ready to be dequeued or a V4L2 event has been received. Polling
+// is done on a dedicated thread, and notifications are delivered in the form of
+// a callback to the listener's sequence.
+//
+// All the methods of this class (with the exception of the constructor) must be
+// called from the same sequence.
+//
+// Note that the service callback may also be called when no particular event
+// occurred due to the way poll() works. It is the responsibility of the caller
+// to call SchedulePoll() again if there may still be pending events.
+class V4L2DevicePoller {
+ public:
+  // Callback to be called when buffer ready/V4L2 event has potentially been
+  // polled. |event| is set if a V4L2 event has been detected.
+  using EventCallback = base::RepeatingCallback<void(bool event)>;
+
+  // Create a poller for |device|, using a thread named |thread_name|.
+  // Notification won't start until |StartPolling()| is called.
+  V4L2DevicePoller(V4L2Device* const device, const std::string& thread_name);
+  ~V4L2DevicePoller();
+
+  // Starts polling. |event_callback| will be posted on the caller's sequence
+  // every time an event occurs. The client is then responsible for consuming
+  // all pending events in that callback. If new events may still happen after
+  // the callback has run, the client must call |SchedulePoll()| again in order
+  // to be notified for them.
+  //
+  // If an error occurs during polling, |error_callback| will be posted on the
+  // caller's sequence.
+  bool StartPolling(EventCallback event_callback,
+                    base::RepeatingClosure error_callback);
+  // Stop polling and stop the thread. The poller won't post any new event to
+  // the caller's sequence after this method has returned.
+  bool StopPolling();
+  // Returns true if currently polling, false otherwise.
+  bool IsPolling() const;
+  // Attempts polling the V4L2 device. This method should be called whenever
+  // doing something that may trigger an event of interest (buffer dequeue or
+  // V4L2 event), for instance queueing a buffer. In the absence of a pending
+  // event, poll() will return immediately and the service callback will be
+  // posted to the caller's sequence. The client is then responsible for calling
+  // this method again when it is interested in receiving events.
+  void SchedulePoll();
+
+ private:
+  // Perform a poll() on |device_| and post either |service_task_| or
+  // |error_callback_| on the client's sequence when poll() returns.
+  void DevicePollTask();
+
+  // V4L2 device we are polling.
+  V4L2Device* const device_;
+  // Thread on which polling is done.
+  base::Thread poll_thread_;
+  // Callback to post to the client's sequence when an event occurs.
+  EventCallback event_callback_;
+  // Closure to post to the client's sequence when an error occurs.
+  base::RepeatingClosure error_callback_;
+  // Client sequence's task runner, where closures are posted.
+  scoped_refptr<base::SequencedTaskRunner> client_task_runner_;
+
+  // Since poll() returns immediately if no buffers have been queued, we cannot
+  // rely on it to pause the polling thread until an event occurs. Instead,
+  // the polling thread will wait on this WaitableEvent (signaled by
+  // |SchedulePoll| before calling poll(), so we only call it when we are
+  // actually waiting for an event.
+  base::WaitableEvent trigger_poll_;
+  // Set to true when we wish to stop polling, instructing the poller thread
+  // to break its loop.
+  std::atomic_bool stop_polling_;
+
+  SEQUENCE_CHECKER(client_sequence_checker_);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_V4L2_V4L2_DEVICE_POLLER_H_
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
index 4a0fe60..a3313c4c 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
@@ -146,7 +146,6 @@
       child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       device_(device),
       decoder_thread_("V4L2SliceVideoDecodeAcceleratorThread"),
-      device_poll_thread_("V4L2SliceVideoDecodeAcceleratorDevicePollThread"),
       video_profile_(VIDEO_CODEC_PROFILE_UNKNOWN),
       input_format_fourcc_(0),
       output_format_fourcc_(0),
@@ -170,7 +169,6 @@
 
   DCHECK(child_task_runner_->BelongsToCurrentThread());
   DCHECK(!decoder_thread_.IsRunning());
-  DCHECK(!device_poll_thread_.IsRunning());
 
   DCHECK(requests_.empty());
   DCHECK(output_buffer_map_.empty());
@@ -384,6 +382,11 @@
         //    executes after all the tasks potentially posted by the IP.
         base::BindOnce(
             [](V4L2SliceVideoDecodeAccelerator* vda) {
+              vda->gl_image_device_ = nullptr;
+              // The image processor's thread was the user of the image
+              // processor device, so let it keep the last reference and destroy
+              // it in its own thread.
+              vda->image_processor_device_ = nullptr;
               vda->image_processor_ = nullptr;
               vda->surfaces_at_ip_ = {};
               vda->decoder_thread_task_runner_->PostTask(
@@ -428,6 +431,10 @@
   base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
       this);
 
+  // Clear the V4L2 devices in the decoder thread so the V4L2Device's
+  // destructor is called from the thread that used it.
+  device_ = nullptr;
+
   DCHECK(surfaces_at_device_.empty());
   DCHECK(surfaces_at_display_.empty());
   DCHECK(decoder_display_queue_.empty());
@@ -749,61 +756,10 @@
   done->Signal();
 }
 
-void V4L2SliceVideoDecodeAccelerator::DevicePollTask(bool poll_device) {
-  DVLOGF(3);
-  DCHECK(device_poll_thread_.task_runner()->BelongsToCurrentThread());
-  TRACE_EVENT0("media,gpu", "V4L2SVDA::DevicePollTask");
-  bool event_pending;
-  if (!device_->Poll(poll_device, &event_pending)) {
-    NOTIFY_ERROR(PLATFORM_FAILURE);
-    return;
-  }
-
-  // All processing should happen on ServiceDeviceTask(), since we shouldn't
-  // touch encoder state from this thread.
-  decoder_thread_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&V4L2SliceVideoDecodeAccelerator::ServiceDeviceTask,
-                     base::Unretained(this)));
-}
-
-void V4L2SliceVideoDecodeAccelerator::ServiceDeviceTask() {
+void V4L2SliceVideoDecodeAccelerator::ServiceDeviceTask(bool event) {
   DVLOGF(4);
   DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread());
 
-  if (IsDestroyPending())
-    return;
-
-  // ServiceDeviceTask() should only ever be scheduled from DevicePollTask().
-
-  Dequeue();
-  SchedulePollIfNeeded();
-}
-
-void V4L2SliceVideoDecodeAccelerator::SchedulePollIfNeeded() {
-  DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread());
-
-  if (!device_poll_thread_.IsRunning()) {
-    DVLOGF(4) << "Device poll thread stopped, will not schedule poll";
-    return;
-  }
-
-  DCHECK(input_queue_->IsStreaming() || output_queue_->IsStreaming());
-
-  if (input_queue_->QueuedBuffersCount() +
-          output_queue_->QueuedBuffersCount() ==
-      0) {
-    DVLOGF(4) << "No buffers queued, will not schedule poll";
-    return;
-  }
-
-  DVLOGF(4) << "Scheduling device poll task";
-
-  device_poll_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&V4L2SliceVideoDecodeAccelerator::DevicePollTask,
-                     base::Unretained(this), true));
-
   DVLOGF(3) << "buffer counts: "
             << "INPUT[" << decoder_input_queue_.size() << "]"
             << " => DEVICE[" << input_queue_->FreeBuffersCount() << "+"
@@ -814,15 +770,17 @@
             << output_buffer_map_.size() << "]"
             << " => DISPLAYQ[" << decoder_display_queue_.size() << "]"
             << " => CLIENT[" << surfaces_at_display_.size() << "]";
+
+  if (IsDestroyPending())
+    return;
+
+  Dequeue();
 }
 
 void V4L2SliceVideoDecodeAccelerator::Enqueue(
     const scoped_refptr<V4L2DecodeSurface>& dec_surface) {
   DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread());
 
-  const int old_inputs_queued = input_queue_->QueuedBuffersCount();
-  const int old_outputs_queued = output_queue_->QueuedBuffersCount();
-
   if (!EnqueueInputRecord(dec_surface.get())) {
     VLOGF(1) << "Failed queueing an input buffer";
     NOTIFY_ERROR(PLATFORM_FAILURE);
@@ -836,9 +794,6 @@
   }
 
   surfaces_at_device_.push(dec_surface);
-
-  if (old_inputs_queued == 0 && old_outputs_queued == 0)
-    SchedulePollIfNeeded();
 }
 
 void V4L2SliceVideoDecodeAccelerator::Dequeue() {
@@ -1021,14 +976,6 @@
 bool V4L2SliceVideoDecodeAccelerator::StartDevicePoll() {
   DVLOGF(3) << "Starting device poll";
   DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread());
-  DCHECK(!device_poll_thread_.IsRunning());
-
-  // Start up the device poll thread and schedule its first DevicePollTask().
-  if (!device_poll_thread_.Start()) {
-    VLOGF(1) << "Device thread failed to start";
-    NOTIFY_ERROR(PLATFORM_FAILURE);
-    return false;
-  }
 
   if (!input_queue_->Streamon())
     return false;
@@ -1036,12 +983,17 @@
   if (!output_queue_->Streamon())
     return false;
 
-  device_poll_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&V4L2SliceVideoDecodeAccelerator::DevicePollTask,
-                     base::Unretained(this), true));
+  // We can use base::Unretained here because the client thread will flush
+  // all tasks posted to the decoder thread before deleting the SVDA.
+  return device_->StartPolling(
+      base::BindRepeating(&V4L2SliceVideoDecodeAccelerator::ServiceDeviceTask,
+                          base::Unretained(this)),
+      base::BindRepeating(&V4L2SliceVideoDecodeAccelerator::OnPollError,
+                          base::Unretained(this)));
+}
 
-  return true;
+void V4L2SliceVideoDecodeAccelerator::OnPollError() {
+  NOTIFY_ERROR(PLATFORM_FAILURE);
 }
 
 bool V4L2SliceVideoDecodeAccelerator::StopDevicePoll() {
@@ -1049,20 +1001,8 @@
   if (decoder_thread_.IsRunning())
     DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread());
 
-  // Signal the DevicePollTask() to stop, and stop the device poll thread.
-  if (!device_->SetDevicePollInterrupt()) {
-    VPLOGF(1) << "SetDevicePollInterrupt(): failed";
-    NOTIFY_ERROR(PLATFORM_FAILURE);
+  if (!device_->StopPolling())
     return false;
-  }
-  device_poll_thread_.Stop();
-  DVLOGF(3) << "Device poll thread stopped";
-
-  // Clear the interrupt now, to be sure.
-  if (!device_->ClearDevicePollInterrupt()) {
-    NOTIFY_ERROR(PLATFORM_FAILURE);
-    return false;
-  }
 
   // We may be called before the queue is acquired.
   if (input_queue_) {
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
index 3149d59..2049b0b 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
@@ -283,19 +283,16 @@
                         uint32_t fourcc);
 
   // Performed on decoder_thread_ as a consequence of poll() on decoder_thread_
-  // returning an event.
-  void ServiceDeviceTask();
-
-  // Schedule poll if we have any buffers queued and the poll thread
-  // is not stopped (on surface set change).
-  void SchedulePollIfNeeded();
+  // returning an event. Typically this means that there are output or capture
+  // buffers that are ready to be dequeued.
+  // |event| is set to true by the poller if a V4L2 event should be dequeued
+  // using VIDIOC_DQEVENT, but this should never happen for the slice API.
+  void ServiceDeviceTask(bool event);
 
   // Attempt to start/stop device_poll_thread_.
   bool StartDevicePoll();
   bool StopDevicePoll();
-
-  // Ran on device_poll_thread_ to wait for device events.
-  void DevicePollTask(bool poll_device);
+  void OnPollError();
 
   // Buffer id for flush buffer, queued by FlushTask().
   const int kFlushBufferId = -2;
@@ -383,9 +380,6 @@
   base::Thread decoder_thread_;
   scoped_refptr<base::SingleThreadTaskRunner> decoder_thread_task_runner_;
 
-  // Thread used to poll the device for events.
-  base::Thread device_poll_thread_;
-
   scoped_refptr<V4L2Queue> input_queue_;
   // Set to true by CreateInputBuffers() if the codec driver supports requests
   bool supports_requests_ = false;
diff --git a/media/gpu/v4l2/v4l2_slice_video_decoder.cc b/media/gpu/v4l2/v4l2_slice_video_decoder.cc
index 4b189f8c..b2d3de0 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decoder.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decoder.cc
@@ -168,7 +168,6 @@
       get_pool_cb_(std::move(get_pool_cb)),
       client_task_runner_(std::move(client_task_runner)),
       decoder_task_runner_(std::move(decoder_task_runner)),
-      device_poll_thread_("V4L2SliceVideoDecoderDevicePollThread"),
       bitstream_id_to_timestamp_(kTimestampCacheSize),
       weak_this_factory_(this) {
   DETACH_FROM_SEQUENCE(client_sequence_checker_);
@@ -546,11 +545,11 @@
   // Streamoff V4L2 queues to drop input and output buffers.
   // If the queues are streaming before reset, then we need to start streaming
   // them after stopping.
-  bool poll_thread_running = device_poll_thread_.IsRunning();
+  bool is_streaming = input_queue_->IsStreaming();
   if (!StopStreamV4L2Queue())
     return;
 
-  if (poll_thread_running) {
+  if (is_streaming) {
     if (!StartStreamV4L2Queue())
       return;
   }
@@ -897,8 +896,6 @@
   }
 
   surfaces_at_device_.push(std::move(dec_surface));
-
-  SchedulePollTaskIfNeeded();
 }
 
 void V4L2SliceVideoDecoder::SurfaceReady(
@@ -930,21 +927,21 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
   DVLOGF(3);
 
-  if (!device_poll_thread_.IsRunning()) {
-    if (!device_poll_thread_.Start()) {
-      VLOGF(1) << "Failed to start device poll thread.";
-      SetState(State::kError);
-      return false;
-    }
-  }
-
   if (!input_queue_->Streamon() || !output_queue_->Streamon()) {
     VLOGF(1) << "Failed to streamon V4L2 queue.";
     SetState(State::kError);
     return false;
   }
 
-  SchedulePollTaskIfNeeded();
+  if (!device_->StartPolling(
+          base::BindRepeating(&V4L2SliceVideoDecoder::ServiceDeviceTask,
+                              weak_this_),
+          base::BindRepeating(&V4L2SliceVideoDecoder::SetState, weak_this_,
+                              State::kError))) {
+    SetState(State::kError);
+    return false;
+  }
+
   return true;
 }
 
@@ -952,19 +949,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
   DVLOGF(3);
 
-  if (!device_poll_thread_.IsRunning())
-    return true;
-
-  if (!device_->SetDevicePollInterrupt()) {
-    VLOGF(1) << "Failed to interrupt device poll.";
-    SetState(State::kError);
-    return false;
-  }
-
-  DVLOGF(3) << "Stop device poll thead";
-  device_poll_thread_.Stop();
-  if (!device_->ClearDevicePollInterrupt()) {
-    VLOGF(1) << "Failed to clear interrupting device poll.";
+  if (!device_->StopPolling()) {
     SetState(State::kError);
     return false;
   }
@@ -981,46 +966,7 @@
   return true;
 }
 
-// Poke when we want to dequeue buffer from V4L2 device
-void V4L2SliceVideoDecoder::SchedulePollTaskIfNeeded() {
-  DVLOGF(3);
-  DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
-  DCHECK(input_queue_->IsStreaming() && output_queue_->IsStreaming());
-
-  if (!device_poll_thread_.IsRunning()) {
-    DVLOGF(4) << "Device poll thread stopped, will not schedule poll";
-    return;
-  }
-
-  if (input_queue_->QueuedBuffersCount() == 0 &&
-      output_queue_->QueuedBuffersCount() == 0) {
-    DVLOGF(4) << "No buffers queued, will not schedule poll";
-    return;
-  }
-
-  device_poll_thread_.task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&V4L2SliceVideoDecoder::DevicePollTask,
-                                base::Unretained(this)));
-}
-
-void V4L2SliceVideoDecoder::DevicePollTask() {
-  DCHECK(device_poll_thread_.task_runner()->RunsTasksInCurrentSequence());
-  DVLOGF(3);
-
-  bool event_pending;
-  if (!device_->Poll(true, &event_pending)) {
-    decoder_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&V4L2SliceVideoDecoder::SetState, weak_this_,
-                                  State::kError));
-    return;
-  }
-
-  decoder_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&V4L2SliceVideoDecoder::ServiceDeviceTask, weak_this_));
-}
-
-void V4L2SliceVideoDecoder::ServiceDeviceTask() {
+void V4L2SliceVideoDecoder::ServiceDeviceTask(bool /* event */) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
   DVLOGF(3) << "Number of queued input buffers: "
             << input_queue_->QueuedBuffersCount()
@@ -1076,8 +1022,6 @@
       break;
   }
 
-  SchedulePollTaskIfNeeded();
-
   if (resume_decode && pause_reason_ == PauseReason::kWaitSubFrameDecoded) {
     decoder_task_runner_->PostTask(
         FROM_HERE,
diff --git a/media/gpu/v4l2/v4l2_slice_video_decoder.h b/media/gpu/v4l2/v4l2_slice_video_decoder.h
index bb6eb9c3..0b6c5996 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decoder.h
+++ b/media/gpu/v4l2/v4l2_slice_video_decoder.h
@@ -202,13 +202,8 @@
   // Stop streaming V4L2 input and output queues. Stop |device_poll_thread_|
   // before stopping streaming.
   bool StopStreamV4L2Queue();
-  // Schedule poll if we have any buffers queued and the poll thread is not
-  // stopped (on surface set change).
-  void SchedulePollTaskIfNeeded();
-  // Ran on device_poll_thread_ to wait for device events.
-  void DevicePollTask();
   // Try to dequeue input and output buffers from device.
-  void ServiceDeviceTask();
+  void ServiceDeviceTask(bool event);
 
   // Get the next bitsream ID.
   int32_t GetNextBitstreamId();
@@ -236,9 +231,6 @@
   // members are manipulated on this thread.
   const scoped_refptr<base::SequencedTaskRunner> decoder_task_runner_;
 
-  // Thread used to poll the device for events.
-  base::Thread device_poll_thread_;
-
   // State of the instance.
   State state_ = State::kUninitialized;
   // Indicates why decoding is currently paused.
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
index e4df520..b7ae150 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
@@ -1924,6 +1924,11 @@
   // First liberate all the frames held by the client.
   buffers_at_client_.clear();
 
+  egl_image_device_ = nullptr;
+
+  // The image processor's thread was the user of the image processor device,
+  // so let it keep the last reference and destroy it in its own thread.
+  image_processor_device_ = nullptr;
   image_processor_ = nullptr;
   while (!buffers_at_ip_.empty())
     buffers_at_ip_.pop();
@@ -1937,6 +1942,10 @@
   decoder_h264_parser_ = nullptr;
   workarounds_.clear();
 
+  // Clear the V4L2 devices in the decoder thread so the V4L2Device's
+  // destructor is called from the thread that used it.
+  device_ = nullptr;
+
   base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
       this);
 }
diff --git a/media/gpu/vaapi/BUILD.gn b/media/gpu/vaapi/BUILD.gn
index c9b11948..f5fa974 100644
--- a/media/gpu/vaapi/BUILD.gn
+++ b/media/gpu/vaapi/BUILD.gn
@@ -45,6 +45,8 @@
     "vaapi_image_decode_accelerator_worker.h",
     "vaapi_image_decoder.cc",
     "vaapi_image_decoder.h",
+    "vaapi_image_processor.cc",
+    "vaapi_image_processor.h",
     "vaapi_jpeg_decoder.cc",
     "vaapi_jpeg_decoder.h",
     "vaapi_jpeg_encoder.cc",
@@ -88,6 +90,7 @@
     "//gpu/ipc/service",
     "//media",
     "//media/gpu:common",
+    "//media/gpu:image_processor_common",
     "//media/gpu:video_frame_mapper_common",
     "//media/gpu/linux:common",
     "//media/parsers",
diff --git a/media/gpu/vaapi/vaapi_image_processor.cc b/media/gpu/vaapi/vaapi_image_processor.cc
new file mode 100644
index 0000000..e1476ee
--- /dev/null
+++ b/media/gpu/vaapi/vaapi_image_processor.cc
@@ -0,0 +1,211 @@
+// 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/vaapi/vaapi_image_processor.h"
+
+#include <va/va.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/stl_util.h"
+#include "base/task/post_task.h"
+#include "media/base/video_frame.h"
+#include "media/gpu/linux/platform_video_frame_utils.h"
+#include "media/gpu/macros.h"
+#include "media/gpu/vaapi/va_surface.h"
+#include "media/gpu/vaapi/vaapi_utils.h"
+#include "media/gpu/vaapi/vaapi_wrapper.h"
+#include "ui/gfx/native_pixmap.h"
+
+namespace media {
+
+namespace {
+// UMA errors that the VaapiImageProcessor class reports.
+enum class VaIPFailure {
+  kVaapiVppError = 0,
+  kMaxValue = kVaapiVppError,
+};
+
+void ReportToUMA(base::RepeatingClosure error_cb, VaIPFailure failure) {
+  base::UmaHistogramEnumeration("Media.VAIP.VppFailure", failure);
+  error_cb.Run();
+}
+
+bool IsSupported(VideoPixelFormat input_format,
+                 VideoPixelFormat output_format,
+                 gfx::Size input_size,
+                 gfx::Size output_size) {
+  const uint32_t input_va_fourcc = VideoPixelFormatToVAFourCC(input_format);
+  if (!VaapiWrapper::IsVppFormatSupported(input_va_fourcc)) {
+    VLOGF(2) << "Unsupported input format: " << input_format << " (VA_FOURCC_"
+             << FourccToString(input_va_fourcc) << ")";
+    return false;
+  }
+
+  const uint32_t output_va_fourcc = VideoPixelFormatToVAFourCC(output_format);
+  if (!VaapiWrapper::IsVppFormatSupported(output_va_fourcc)) {
+    VLOGF(2) << "Unsupported output format: " << output_format << " (VA_FOURCC_"
+             << FourccToString(output_va_fourcc) << ")";
+    return false;
+  }
+
+  if (!VaapiWrapper::IsVppResolutionAllowed(input_size)) {
+    VLOGF(2) << "Unsupported input size: " << input_size.ToString();
+    return false;
+  }
+
+  if (!VaapiWrapper::IsVppResolutionAllowed(output_size)) {
+    VLOGF(2) << "Unsupported output size: " << output_size.ToString();
+    return false;
+  }
+
+  return true;
+}
+
+void ProcessTask(scoped_refptr<VideoFrame> input_frame,
+                 scoped_refptr<VideoFrame> output_frame,
+                 ImageProcessor::FrameReadyCB cb,
+                 scoped_refptr<VaapiWrapper> vaapi_wrapper) {
+  DVLOGF(4);
+
+  auto src_va_surface =
+      vaapi_wrapper->CreateVASurfaceForVideoFrame(input_frame.get());
+  auto dst_va_surface =
+      vaapi_wrapper->CreateVASurfaceForVideoFrame(output_frame.get());
+  if (!src_va_surface || !dst_va_surface) {
+    // Failed to create VASurface for frames. |cb| isn't executed in the case.
+    return;
+  }
+  // VA-API performs pixel format conversion and scaling without any filters.
+  vaapi_wrapper->BlitSurface(std::move(src_va_surface),
+                             std::move(dst_va_surface));
+  std::move(cb).Run(std::move(output_frame));
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<VaapiImageProcessor> VaapiImageProcessor::Create(
+    const ImageProcessor::PortConfig& input_config,
+    const ImageProcessor::PortConfig& output_config,
+    const std::vector<ImageProcessor::OutputMode>& preferred_output_modes,
+    const base::RepeatingClosure& error_cb) {
+// VaapiImageProcessor supports ChromeOS only.
+#if !defined(OS_CHROMEOS)
+  return nullptr;
+#endif
+
+  const VideoFrameLayout& input_layout = input_config.layout;
+  const VideoFrameLayout& output_layout = output_config.layout;
+  if (!IsSupported(input_layout.format(), output_layout.format(),
+                   input_config.layout.coded_size(),
+                   output_config.layout.coded_size())) {
+    return nullptr;
+  }
+
+  if (!base::Contains(input_config.preferred_storage_types,
+                      VideoFrame::STORAGE_DMABUFS) ||
+      !base::Contains(output_config.preferred_storage_types,
+                      VideoFrame::STORAGE_DMABUFS)) {
+    VLOGF(2) << "VaapiImageProcessor supports Dmabuf-backed VideoFrame only "
+             << "for both input and output";
+    return nullptr;
+  }
+
+  if (!base::Contains(preferred_output_modes, OutputMode::IMPORT)) {
+    VLOGF(2) << "VaapiImageProcessor only supports IMPORT mode.";
+    return nullptr;
+  }
+
+  auto vaapi_wrapper = VaapiWrapper::Create(
+      VaapiWrapper::kVideoProcess, VAProfileNone,
+      base::BindRepeating(&ReportToUMA, error_cb, VaIPFailure::kVaapiVppError));
+  if (!vaapi_wrapper) {
+    VLOGF(1) << "Failed to create VaapiWrapper";
+    return nullptr;
+  }
+
+  // We should restrict the acceptable VideoFrameLayout for input and output
+  // both to the one returned by GetPlatformVideoFrameLayout(). However,
+  // ImageProcessorFactory interface doesn't provide information about what
+  // ImageProcessor will be used for. (e.g. format conversion after decoding and
+  // scaling before encoding). Thus we cannot execute
+  // GetPlatformVideoFrameLayout() with a proper gfx::BufferUsage.
+  // TODO(crbug.com/898423): Adjust layout once ImageProcessor provide the use
+  // scenario.
+  return base::WrapUnique(new VaapiImageProcessor(input_layout, output_layout,
+                                                  std::move(vaapi_wrapper)));
+}
+
+VaapiImageProcessor::VaapiImageProcessor(
+    const VideoFrameLayout& input_layout,
+    const VideoFrameLayout& output_layout,
+    scoped_refptr<VaapiWrapper> vaapi_wrapper)
+    : ImageProcessor(input_layout,
+                     VideoFrame::STORAGE_DMABUFS,
+                     output_layout,
+                     VideoFrame::STORAGE_DMABUFS,
+                     OutputMode::IMPORT),
+      processor_task_runner_(base::CreateSequencedTaskRunner(
+          base::TaskTraits{base::ThreadPool()})),
+      vaapi_wrapper_(std::move(vaapi_wrapper)) {}
+
+VaapiImageProcessor::~VaapiImageProcessor() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+}
+
+bool VaapiImageProcessor::ProcessInternal(
+    scoped_refptr<VideoFrame> input_frame,
+    scoped_refptr<VideoFrame> output_frame,
+    FrameReadyCB cb) {
+  DVLOGF(4);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+  DCHECK(input_frame);
+  DCHECK(output_frame);
+
+  const VideoFrameLayout& input_frame_layout = input_frame->layout();
+  if (input_frame_layout.format() != input_layout_.format() ||
+      input_frame_layout.coded_size() != input_layout_.coded_size()) {
+    VLOGF(1) << "Invalid input_frame->layout=" << input_frame->layout()
+             << ", input_layout_=" << input_layout_;
+    return false;
+  }
+
+  const VideoFrameLayout& output_frame_layout = output_frame->layout();
+  if (output_frame_layout.format() != output_layout_.format() ||
+      output_frame_layout.coded_size() != output_layout_.coded_size()) {
+    VLOGF(1) << "Invalid output_frame->layout=" << output_frame->layout()
+             << ", output_layout_=" << output_layout_;
+    return false;
+  }
+
+  if (input_frame->storage_type() != input_storage_type()) {
+    VLOGF(1) << "Invalid input_frame->storage_type="
+             << input_frame->storage_type()
+             << ", input_storage_type=" << input_storage_type();
+    return false;
+  }
+  if (output_frame->storage_type() != output_storage_type()) {
+    VLOGF(1) << "Invalid output_frame->storage_type="
+             << output_frame->storage_type()
+             << ", output_storage_type=" << output_storage_type();
+    return false;
+  }
+
+  process_task_tracker_.PostTask(
+      processor_task_runner_.get(), FROM_HERE,
+      base::BindOnce(&ProcessTask, std::move(input_frame),
+                     std::move(output_frame), std::move(cb), vaapi_wrapper_));
+  return true;
+}
+
+bool VaapiImageProcessor::Reset() {
+  VLOGF(2);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+  process_task_tracker_.TryCancelAll();
+  return true;
+}
+}  // namespace media
diff --git a/media/gpu/vaapi/vaapi_image_processor.h b/media/gpu/vaapi/vaapi_image_processor.h
new file mode 100644
index 0000000..69a0082b
--- /dev/null
+++ b/media/gpu/vaapi/vaapi_image_processor.h
@@ -0,0 +1,68 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_VAAPI_VAAPI_IMAGE_PROCESSOR_H_
+#define MEDIA_GPU_VAAPI_VAAPI_IMAGE_PROCESSOR_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "build/build_config.h"
+#include "media/gpu/image_processor.h"
+
+namespace media {
+
+class VaapiWrapper;
+
+// ImageProcessor that is hardware accelerated with VA-API. This ImageProcessor
+// supports DmaBuf only for both input and output.
+class VaapiImageProcessor : public ImageProcessor {
+ public:
+  // Factory method to create VaapiImageProcessor for a buffer conversion
+  // specified by |input_config| and |output_config|. Provided |error_cb| will
+  // be posted to the same thread that executes Create(), if an error occurs
+  // after initialization.
+  // Returns nullptr if it fails to create VaapiImageProcessor.
+  static std::unique_ptr<VaapiImageProcessor> Create(
+      const ImageProcessor::PortConfig& input_config,
+      const ImageProcessor::PortConfig& output_config,
+      const std::vector<ImageProcessor::OutputMode>& preferred_output_modes,
+      const base::RepeatingClosure& error_cb);
+
+  // ImageProcessor implementation.
+  ~VaapiImageProcessor() override;
+  bool Reset() override;
+
+ private:
+  VaapiImageProcessor(const VideoFrameLayout& input_layout,
+                      const VideoFrameLayout& output_layout,
+                      scoped_refptr<VaapiWrapper> vaapi_wrapper);
+
+  // ImageProcessor implementation.
+  bool ProcessInternal(scoped_refptr<VideoFrame> input_frame,
+                       scoped_refptr<VideoFrame> output_frame,
+                       FrameReadyCB cb) override;
+
+  // Sequence task runner in which the buffer conversion is performed.
+  const scoped_refptr<base::SequencedTaskRunner> processor_task_runner_;
+
+  // CancelableTaskTracker for posted tasks to |processor_task_runner_|. We
+  // can't use CancelableCallback or WeakPtr because we may need to cancel tasks
+  // from outside |processor_task_runner_|.
+  base::CancelableTaskTracker process_task_tracker_;
+
+  const scoped_refptr<VaapiWrapper> vaapi_wrapper_;
+
+  SEQUENCE_CHECKER(client_sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(VaapiImageProcessor);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_VAAPI_VAAPI_IMAGE_PROCESSOR_H_
diff --git a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
index 6ab2963..f697e0a3 100644
--- a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
@@ -156,8 +156,9 @@
     input_size_ = gfx::Size();
 
     std::vector<VASurfaceID> va_surfaces;
-    if (!vaapi_wrapper_->CreateContextAndSurfaces(va_format, input_size, 1,
-                                                  &va_surfaces)) {
+    if (!vaapi_wrapper_->CreateContextAndSurfaces(
+            va_format, input_size, VaapiWrapper::SurfaceUsageHint::kGeneric, 1,
+            &va_surfaces)) {
       VLOGF(1) << "Failed to create VA surface";
       notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
       return;
@@ -308,7 +309,8 @@
 
     std::vector<VASurfaceID> va_surfaces;
     if (!vaapi_wrapper_->CreateContextAndSurfaces(
-            VA_RT_FORMAT_YUV420, input_size, 1, &va_surfaces)) {
+            VA_RT_FORMAT_YUV420, input_size,
+            VaapiWrapper::SurfaceUsageHint::kGeneric, 1, &va_surfaces)) {
       VLOGF(1) << "Failed to create VA surface";
       notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
       return;
diff --git a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
index 6d68e85..937eecd 100644
--- a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
@@ -48,8 +48,6 @@
 
 namespace {
 
-constexpr uint32_t kInvalidVaFourcc = 0u;
-
 // UMA errors that the VaapiMjpegDecodeAccelerator class reports.
 enum VAJDAFailure {
   VAAPI_ERROR = 0,
@@ -103,32 +101,6 @@
   }
   return base::strict_cast<size_t>(image->data_size) >= min_size;
 }
-
-static uint32_t VideoPixelFormatToVAFourCC(VideoPixelFormat format) {
-  switch (format) {
-    case PIXEL_FORMAT_I420:
-      return VA_FOURCC_I420;
-    case PIXEL_FORMAT_YV12:
-      return VA_FOURCC_YV12;
-    case PIXEL_FORMAT_NV12:
-      return VA_FOURCC_NV12;
-    case PIXEL_FORMAT_NV21:
-      return VA_FOURCC_NV21;
-    case PIXEL_FORMAT_YUY2:
-      return VA_FOURCC_YUY2;
-    case PIXEL_FORMAT_ARGB:
-      return VA_FOURCC_ARGB;
-    case PIXEL_FORMAT_XRGB:
-      return VA_FOURCC_XRGB;
-    case PIXEL_FORMAT_ABGR:
-      return VA_FOURCC_ABGR;
-    case PIXEL_FORMAT_XBGR:
-      return VA_FOURCC_XBGR;
-    default:
-      return kInvalidVaFourcc;
-  }
-}
-
 }  // namespace
 
 void VaapiMjpegDecodeAccelerator::NotifyError(int32_t task_id, Error error) {
diff --git a/media/gpu/vaapi/vaapi_utils.cc b/media/gpu/vaapi/vaapi_utils.cc
index 641bb01..2531660 100644
--- a/media/gpu/vaapi/vaapi_utils.cc
+++ b/media/gpu/vaapi/vaapi_utils.cc
@@ -301,4 +301,32 @@
       VASliceDataBufferType, frame_header.frame_size, frame_header.data);
 }
 
+uint32_t VideoPixelFormatToVAFourCC(VideoPixelFormat format) {
+  // Formats in media::VideoPixelFormat are specified in little-endian order.
+  // See
+  // https://docs.google.com/spreadsheets/d/1E8yxvnAkG6oXhe1bnekhSkYzQQSUwb-kJapEtWmyLe4
+  switch (format) {
+    case VideoPixelFormat::PIXEL_FORMAT_I420:
+      return VA_FOURCC_I420;
+    case VideoPixelFormat::PIXEL_FORMAT_NV12:
+      return VA_FOURCC_NV12;
+    case VideoPixelFormat::PIXEL_FORMAT_NV21:
+      return VA_FOURCC_NV21;
+    case VideoPixelFormat::PIXEL_FORMAT_YV12:
+      return VA_FOURCC_YV12;
+    case VideoPixelFormat::PIXEL_FORMAT_YUY2:
+      return VA_FOURCC_YUY2;
+    case VideoPixelFormat::PIXEL_FORMAT_ABGR:
+      return VA_FOURCC_RGBA;
+    case VideoPixelFormat::PIXEL_FORMAT_XBGR:
+      return VA_FOURCC_RGBX;
+    case VideoPixelFormat::PIXEL_FORMAT_ARGB:
+      return VA_FOURCC_BGRA;
+    case VideoPixelFormat::PIXEL_FORMAT_RGB24:
+      return VA_FOURCC_BGRX;
+    default:
+      DVLOG(3) << "Unsupported format: " << format;
+      return kInvalidVaFourcc;
+  }
+}
 }  // namespace media
diff --git a/media/gpu/vaapi/vaapi_utils.h b/media/gpu/vaapi/vaapi_utils.h
index 9cc894b3..564afed 100644
--- a/media/gpu/vaapi/vaapi_utils.h
+++ b/media/gpu/vaapi/vaapi_utils.h
@@ -9,6 +9,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/thread_annotations.h"
+#include "media/base/video_types.h"
 #include "ui/gfx/geometry/size.h"
 
 // Forward declarations taken verbatim from <va/va.h>
@@ -29,6 +30,8 @@
 struct VAContextAndScopedVASurfaceDeleter;
 struct Vp8FrameHeader;
 
+constexpr uint32_t kInvalidVaFourcc = 0u;
+
 // Class to map a given VABuffer, identified by |buffer_id|, for its lifetime.
 // This class must operate under |lock_| acquired.
 class ScopedVABufferMapping {
@@ -122,6 +125,9 @@
                            const Vp8FrameHeader& frame_header,
                            const Vp8ReferenceFrameVector& reference_frames);
 
+// Returns a VA_FOURCC corresponding to |format|. Returns kInvalidVaFourcc if no
+// corresponding VA_FOURCC is found,
+uint32_t VideoPixelFormatToVAFourCC(VideoPixelFormat format);
 }  // namespace media
 
 #endif  // MEDIA_GPU_VAAPI_VAAPI_UTILS_H_
diff --git a/media/gpu/vaapi/vaapi_utils_unittest.cc b/media/gpu/vaapi/vaapi_utils_unittest.cc
index d3f4a5e..bec1c92 100644
--- a/media/gpu/vaapi/vaapi_utils_unittest.cc
+++ b/media/gpu/vaapi/vaapi_utils_unittest.cc
@@ -56,7 +56,8 @@
   std::vector<VASurfaceID> va_surfaces;
   const gfx::Size coded_size(64, 64);
   ASSERT_TRUE(vaapi_wrapper_->CreateContextAndSurfaces(
-      VA_RT_FORMAT_YUV420, coded_size, 1, &va_surfaces));
+      VA_RT_FORMAT_YUV420, coded_size, VaapiWrapper::SurfaceUsageHint::kGeneric,
+      1, &va_surfaces));
   ASSERT_EQ(va_surfaces.size(), 1u);
 
   std::unique_ptr<ScopedVAImage> scoped_image;
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
index 9a22e8f..17594e8 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
@@ -700,11 +700,12 @@
             : pictures_.size();
     CHECK_NE(requested_num_surfaces, 0u);
     va_surface_ids.clear();
-    RETURN_AND_NOTIFY_ON_FAILURE(vaapi_wrapper_->CreateContextAndSurfaces(
-                                     va_surface_format_, requested_pic_size_,
-                                     requested_num_surfaces, &va_surface_ids),
-                                 "Failed creating VA Surfaces",
-                                 PLATFORM_FAILURE, );
+    RETURN_AND_NOTIFY_ON_FAILURE(
+        vaapi_wrapper_->CreateContextAndSurfaces(
+            va_surface_format_, requested_pic_size_,
+            VaapiWrapper::SurfaceUsageHint::kVideoDecoder,
+            requested_num_surfaces, &va_surface_ids),
+        "Failed creating VA Surfaces", PLATFORM_FAILURE, );
   }
 
   available_va_surfaces_.assign(va_surface_ids.begin(), va_surface_ids.end());
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
index 70ec436..86b0720f 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
@@ -68,9 +68,12 @@
 class MockVaapiWrapper : public VaapiWrapper {
  public:
   MockVaapiWrapper() = default;
-  MOCK_METHOD4(
-      CreateContextAndSurfaces,
-      bool(unsigned int, const gfx::Size&, size_t, std::vector<VASurfaceID>*));
+  MOCK_METHOD5(CreateContextAndSurfaces,
+               bool(unsigned int,
+                    const gfx::Size&,
+                    SurfaceUsageHint,
+                    size_t,
+                    std::vector<VASurfaceID>*));
   MOCK_METHOD1(CreateContext, bool(const gfx::Size&));
   MOCK_METHOD1(DestroyContextAndSurfaces, void(std::vector<VASurfaceID>));
 
@@ -294,9 +297,11 @@
       const size_t kNumReferenceFrames = 1 + num_pictures / 2;
       EXPECT_CALL(
           *mock_vaapi_wrapper_,
-          CreateContextAndSurfaces(_, picture_size, kNumReferenceFrames, _))
+          CreateContextAndSurfaces(
+              _, picture_size, VaapiWrapper::SurfaceUsageHint::kVideoDecoder,
+              kNumReferenceFrames, _))
           .WillOnce(DoAll(
-              WithArg<3>(Invoke([kNumReferenceFrames](
+              WithArg<4>(Invoke([kNumReferenceFrames](
                                     std::vector<VASurfaceID>* va_surface_ids) {
                 va_surface_ids->resize(kNumReferenceFrames);
               })),
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index ff34753..dd6aa94 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -369,6 +369,7 @@
 
   if (!vaapi_wrapper_->CreateContextAndSurfaces(
           kVaSurfaceFormat, coded_size_,
+          VaapiWrapper::SurfaceUsageHint::kVideoEncoder,
           (num_frames_in_flight + 1) * va_surfaces_per_video_frame_,
           &available_va_surface_ids_)) {
     NOTIFY_ERROR(kPlatformFailureError, "Failed creating VASurfaces");
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index e134cc24..e99850d 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -107,6 +107,8 @@
       return VA_FOURCC_BGRA;
     case gfx::BufferFormat::RGBX_8888:
       return VA_FOURCC_RGBX;
+    case gfx::BufferFormat::RGBA_8888:
+      return VA_FOURCC_RGBA;
     case gfx::BufferFormat::YVU_420:
       return VA_FOURCC_YV12;
     case gfx::BufferFormat::YUV_420_BIPLANAR:
@@ -1264,17 +1266,21 @@
 }
 
 // static
-bool VaapiWrapper::IsVppSupportedForJpegDecodedSurfaceToFourCC(
-    unsigned int rt_format,
-    uint32_t fourcc) {
-  if (!IsDecodingSupportedForInternalFormat(VAProfileJPEGBaseline, rt_format))
-    return false;
-
+bool VaapiWrapper::IsVppFormatSupported(uint32_t va_fourcc) {
   VASupportedProfiles::ProfileInfo profile_info;
   if (!VASupportedProfiles::Get().IsProfileSupported(
           kVideoProcess, VAProfileNone, &profile_info)) {
     return false;
   }
+  return base::Contains(profile_info.pixel_formats, va_fourcc);
+}
+
+// static
+bool VaapiWrapper::IsVppSupportedForJpegDecodedSurfaceToFourCC(
+    unsigned int rt_format,
+    uint32_t fourcc) {
+  if (!IsDecodingSupportedForInternalFormat(VAProfileJPEGBaseline, rt_format))
+    return false;
 
   // Workaround: for Mesa VAAPI driver, VPP only supports internal surface
   // format for 4:2:0 JPEG image.
@@ -1285,7 +1291,7 @@
     return false;
   }
 
-  return base::Contains(profile_info.pixel_formats, fourcc);
+  return IsVppFormatSupported(fourcc);
 }
 
 // static
@@ -1311,6 +1317,7 @@
     case gfx::BufferFormat::BGRX_8888:
     case gfx::BufferFormat::BGRA_8888:
     case gfx::BufferFormat::RGBX_8888:
+    case gfx::BufferFormat::RGBA_8888:
       return VA_RT_FORMAT_RGB32;
     case gfx::BufferFormat::YVU_420:
     case gfx::BufferFormat::YUV_420_BIPLANAR:
@@ -1324,6 +1331,7 @@
 bool VaapiWrapper::CreateContextAndSurfaces(
     unsigned int va_format,
     const gfx::Size& size,
+    SurfaceUsageHint surface_usage_hint,
     size_t num_surfaces,
     std::vector<VASurfaceID>* va_surfaces) {
   DVLOG(2) << "Creating " << num_surfaces << " surfaces";
@@ -1335,8 +1343,10 @@
     return false;
   }
 
-  if (!CreateSurfaces(va_format, size, num_surfaces, va_surfaces))
+  if (!CreateSurfaces(va_format, size, surface_usage_hint, num_surfaces,
+                      va_surfaces)) {
     return false;
+  }
 
   const bool success = CreateContext(size);
   if (!success)
@@ -2069,6 +2079,7 @@
 
 bool VaapiWrapper::CreateSurfaces(unsigned int va_format,
                                   const gfx::Size& size,
+                                  SurfaceUsageHint usage_hint,
                                   size_t num_surfaces,
                                   std::vector<VASurfaceID>* va_surfaces) {
   DVLOG(2) << "Creating " << num_surfaces << " " << size.ToString()
@@ -2077,13 +2088,29 @@
   DCHECK(va_surfaces->empty());
 
   va_surfaces->resize(num_surfaces);
+  VASurfaceAttrib attribute{};
+  attribute.type = VASurfaceAttribUsageHint;
+  attribute.flags = VA_SURFACE_ATTRIB_SETTABLE;
+  attribute.value.type = VAGenericValueTypeInteger;
+  switch (usage_hint) {
+    case SurfaceUsageHint::kVideoDecoder:
+      attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_DECODER;
+      break;
+    case SurfaceUsageHint::kVideoEncoder:
+      attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER;
+      break;
+    case SurfaceUsageHint::kGeneric:
+      attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
+      break;
+  }
+
   VAStatus va_res;
   {
     base::AutoLock auto_lock(*va_lock_);
-    va_res = vaCreateSurfaces(va_display_, va_format,
-                              base::checked_cast<unsigned int>(size.width()),
-                              base::checked_cast<unsigned int>(size.height()),
-                              va_surfaces->data(), num_surfaces, NULL, 0);
+    va_res = vaCreateSurfaces(
+        va_display_, va_format, base::checked_cast<unsigned int>(size.width()),
+        base::checked_cast<unsigned int>(size.height()), va_surfaces->data(),
+        num_surfaces, &attribute, 1u);
   }
   VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed");
   return va_res == VA_STATUS_SUCCESS;
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h
index 6c00f57..27b3fe6 100644
--- a/media/gpu/vaapi/vaapi_wrapper.h
+++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -94,6 +94,13 @@
     kCodecModeMax,
   };
 
+  // This is enum associated with VASurfaceAttribUsageHint.
+  enum class SurfaceUsageHint : uint8_t {
+    kVideoDecoder,
+    kVideoEncoder,
+    kGeneric,
+  };
+
   using InternalFormats = struct {
     bool yuv420 : 1;
     bool yuv422 : 1;
@@ -168,6 +175,9 @@
   // supported, false otherwise.
   static bool IsVppResolutionAllowed(const gfx::Size& size);
 
+  // Returns true if the VPP supports converting from/to |fourcc|.
+  static bool IsVppFormatSupported(uint32_t fourcc);
+
   // Returns true if VPP supports the format conversion from a JPEG decoded
   // internal surface to a FOURCC. |rt_format| corresponds to the JPEG's
   // subsampling format. |fourcc| is the output surface's FOURCC.
@@ -186,13 +196,15 @@
 
   static uint32_t BufferFormatToVARTFormat(gfx::BufferFormat fmt);
 
-  // Creates |num_surfaces| VASurfaceIDs of |va_format| and |size| and, if
-  // successful, creates a |va_context_id_| of the same size. Returns true if
-  // successful, with the created IDs in |va_surfaces|. The client is
-  // responsible for destroying |va_surfaces| via DestroyContextAndSurfaces() to
-  // free the allocated surfaces.
+  // Creates |num_surfaces| VASurfaceIDs of |va_format|, |size| and
+  // |surface_usage_hint| and, if successful, creates a |va_context_id_| of the
+  // same size. |surface_usage_hint| may affect an alignment and tiling of the
+  // created surface. Returns true if successful, with the created IDs in
+  // |va_surfaces|. The client is responsible for destroying |va_surfaces| via
+  // DestroyContextAndSurfaces() to free the allocated surfaces.
   virtual bool CreateContextAndSurfaces(unsigned int va_format,
                                         const gfx::Size& size,
+                                        SurfaceUsageHint surface_usage_hint,
                                         size_t num_surfaces,
                                         std::vector<VASurfaceID>* va_surfaces);
 
@@ -390,6 +402,7 @@
   // Fills |va_surfaces| and returns true if successful, or returns false.
   bool CreateSurfaces(unsigned int va_format,
                       const gfx::Size& size,
+                      SurfaceUsageHint usage_hint,
                       size_t num_surfaces,
                       std::vector<VASurfaceID>* va_surfaces);
 
diff --git a/media/mojo/DEPS b/media/mojo/DEPS
index 258ba11..ce05c48 100644
--- a/media/mojo/DEPS
+++ b/media/mojo/DEPS
@@ -12,6 +12,11 @@
   "+services/metrics",
   "+services/service_manager",
 
+
+  # For converting GpuMemoryBufferHandle to GpuMemoryBuffer in
+  # video_frame_mojom_traits.cc.
+  "+gpu/ipc/common/gpu_memory_buffer_support.h",
+
   # media/mojo is not part of "media" target and should not use MEDIA_EXPORT.
   "-media/base/media_export.h"
 ]
diff --git a/media/mojo/clients/mojo_video_encode_accelerator.cc b/media/mojo/clients/mojo_video_encode_accelerator.cc
index eb54e73..a553f17 100644
--- a/media/mojo/clients/mojo_video_encode_accelerator.cc
+++ b/media/mojo/clients/mojo_video_encode_accelerator.cc
@@ -125,15 +125,24 @@
   DCHECK(vea_.is_bound());
 
 #if defined(OS_LINUX)
+  // TODO(crbug.com/1003197): Remove this once we stop supporting STORAGE_DMABUF
+  // in VideoEncodeAccelerator.
   if (frame->storage_type() == VideoFrame::STORAGE_DMABUFS) {
     DCHECK(frame->HasDmaBufs());
     vea_->Encode(
-        std::move(frame), force_keyframe,
+        frame, force_keyframe,
         base::BindOnce(base::DoNothing::Once<scoped_refptr<VideoFrame>>(),
                        frame));
     return;
   }
 #endif
+  if (frame->storage_type() == VideoFrame::STORAGE_GPU_MEMORY_BUFFER) {
+    vea_->Encode(
+        frame, force_keyframe,
+        base::BindOnce(base::DoNothing::Once<scoped_refptr<VideoFrame>>(),
+                       frame));
+    return;
+  }
 
   DCHECK_EQ(PIXEL_FORMAT_I420, frame->format());
   DCHECK_EQ(VideoFrame::STORAGE_SHMEM, frame->storage_type());
diff --git a/media/mojo/mojom/BUILD.gn b/media/mojo/mojom/BUILD.gn
index b8dfc96d..d3788eac 100644
--- a/media/mojo/mojom/BUILD.gn
+++ b/media/mojo/mojom/BUILD.gn
@@ -133,6 +133,7 @@
   deps = [
     "//base",
     "//base/test:test_support",
+    "//media:test_support",
     "//media/mojo:test_support",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/media/mojo/mojom/media_types.mojom b/media/mojo/mojom/media_types.mojom
index fee3497..1068f46 100644
--- a/media/mojo/mojom/media_types.mojom
+++ b/media/mojo/mojom/media_types.mojom
@@ -9,6 +9,7 @@
 import "mojo/public/mojom/base/time.mojom";
 import "mojo/public/mojom/base/values.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
+import "ui/gfx/mojom/buffer_types.mojom";
 import "ui/gfx/mojom/color_space.mojom";
 
 // See media/base/audio_codecs.h for descriptions.
@@ -275,6 +276,7 @@
   EosVideoFrameData eos_data;
   SharedBufferVideoFrameData shared_buffer_data;
   DmabufVideoFrameData dmabuf_data;
+  GpuMemoryBufferVideoFrameData gpu_memory_buffer_data;
   MailboxVideoFrameData mailbox_data;
 };
 
@@ -304,6 +306,11 @@
   array<handle> dmabuf_fds;
 };
 
+struct GpuMemoryBufferVideoFrameData {
+  gfx.mojom.GpuMemoryBufferHandle gpu_memory_buffer_handle;
+  array<gpu.mojom.MailboxHolder, 4> mailbox_holder;
+};
+
 // This defines video frame data stored in texture mailboxes.
 struct MailboxVideoFrameData {
   // Size must be kept in sync with media::VideoFrame::kMaxPlanes.
diff --git a/media/mojo/mojom/video_frame.typemap b/media/mojo/mojom/video_frame.typemap
index 36b853a..d465abc 100644
--- a/media/mojo/mojom/video_frame.typemap
+++ b/media/mojo/mojom/video_frame.typemap
@@ -27,6 +27,7 @@
 ]
 
 deps = [
+  "//gpu/ipc/common:common",
   "//gpu/ipc/common:mojom_traits",
   "//media/base/ipc",
   "//media/mojo/common:mojo_shared_buffer_video_frame",
diff --git a/media/mojo/mojom/video_frame_mojom_traits.cc b/media/mojo/mojom/video_frame_mojom_traits.cc
index 46995e0..d1ec35e 100644
--- a/media/mojo/mojom/video_frame_mojom_traits.cc
+++ b/media/mojo/mojom/video_frame_mojom_traits.cc
@@ -9,11 +9,14 @@
 
 #include "base/logging.h"
 #include "build/build_config.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
+#include "media/base/format_utils.h"
 #include "media/mojo/common/mojo_shared_buffer_video_frame.h"
 #include "mojo/public/cpp/base/time_mojom_traits.h"
 #include "mojo/public/cpp/base/values_mojom_traits.h"
 #include "mojo/public/cpp/system/handle.h"
 #include "mojo/public/cpp/system/platform_handle.h"
+#include "ui/gfx/mojom/buffer_types_mojom_traits.h"
 #include "ui/gfx/mojom/color_space_mojom_traits.h"
 
 namespace mojo {
@@ -69,12 +72,20 @@
   }
 #endif
 
-  if (input->HasTextures()) {
-    std::vector<gpu::MailboxHolder> mailbox_holder(
-        media::VideoFrame::kMaxPlanes);
-    size_t num_planes = media::VideoFrame::NumPlanes(input->format());
-    for (size_t i = 0; i < num_planes; i++)
-      mailbox_holder[i] = input->mailbox_holder(i);
+  std::vector<gpu::MailboxHolder> mailbox_holder(media::VideoFrame::kMaxPlanes);
+  size_t num_planes = media::VideoFrame::NumPlanes(input->format());
+  DCHECK_LE(num_planes, mailbox_holder.size());
+  for (size_t i = 0; i < num_planes; i++)
+    mailbox_holder[i] = input->mailbox_holder(i);
+
+  if (input->storage_type() == media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER) {
+    gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle;
+    if (input->HasGpuMemoryBuffer())
+      gpu_memory_buffer_handle = input->GetGpuMemoryBuffer()->CloneHandle();
+    return media::mojom::VideoFrameData::NewGpuMemoryBufferData(
+        media::mojom::GpuMemoryBufferVideoFrameData::New(
+            std::move(gpu_memory_buffer_handle), std::move(mailbox_holder)));
+  } else if (input->HasTextures()) {
     return media::mojom::VideoFrameData::NewMailboxData(
         media::mojom::MailboxVideoFrameData::New(
             std::move(mailbox_holder), std::move(input->ycbcr_info())));
@@ -186,6 +197,47 @@
     frame = media::VideoFrame::WrapExternalDmabufs(
         *layout, visible_rect, natural_size, std::move(dmabuf_fds), timestamp);
 #endif
+  } else if (data.is_gpu_memory_buffer_data()) {
+    media::mojom::GpuMemoryBufferVideoFrameDataDataView gpu_memory_buffer_data;
+    data.GetGpuMemoryBufferDataDataView(&gpu_memory_buffer_data);
+
+    gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle;
+    if (!gpu_memory_buffer_data.ReadGpuMemoryBufferHandle(
+            &gpu_memory_buffer_handle)) {
+      return false;
+    }
+
+    std::vector<gpu::MailboxHolder> mailbox_holder;
+    if (!gpu_memory_buffer_data.ReadMailboxHolder(&mailbox_holder)) {
+      DLOG(WARNING) << "Failed to get mailbox holder";
+    }
+    if (mailbox_holder.size() > media::VideoFrame::kMaxPlanes) {
+      DLOG(ERROR) << "The size of mailbox holder is too large: "
+                  << mailbox_holder.size();
+      return false;
+    }
+
+    gpu::MailboxHolder mailbox_holder_array[media::VideoFrame::kMaxPlanes];
+    for (size_t i = 0; i < mailbox_holder.size(); i++)
+      mailbox_holder_array[i] = mailbox_holder[i];
+
+    base::Optional<gfx::BufferFormat> buffer_format =
+        VideoPixelFormatToGfxBufferFormat(format);
+    if (!buffer_format)
+      return false;
+
+    gpu::GpuMemoryBufferSupport support;
+    std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
+        support.CreateGpuMemoryBufferImplFromHandle(
+            std::move(gpu_memory_buffer_handle), coded_size, *buffer_format,
+            gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE,
+            base::NullCallback());
+    if (!gpu_memory_buffer)
+      return false;
+
+    frame = media::VideoFrame::WrapExternalGpuMemoryBuffer(
+        visible_rect, natural_size, std::move(gpu_memory_buffer),
+        mailbox_holder_array, media::VideoFrame::ReleaseMailboxCB(), timestamp);
   } else if (data.is_mailbox_data()) {
     media::mojom::MailboxVideoFrameDataDataView mailbox_data;
     data.GetMailboxDataDataView(&mailbox_data);
diff --git a/media/mojo/mojom/video_frame_mojom_traits_unittest.cc b/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
index f66ff81..945097e 100644
--- a/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
+++ b/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
@@ -7,12 +7,14 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/test/task_environment.h"
+#include "build/build_config.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
 #include "gpu/command_buffer/common/sync_token.h"
 #include "media/base/video_frame.h"
 #include "media/mojo/common/mojo_shared_buffer_video_frame.h"
 #include "media/mojo/mojom/traits_test_service.mojom.h"
+#include "media/video/fake_gpu_memory_buffer.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/system/buffer.h"
@@ -154,4 +156,41 @@
   ASSERT_EQ(frame->mailbox_holder(0).mailbox, mailbox);
 }
 
+// defined(OS_LINUX) because media::FakeGpuMemoryBuffer supports
+// NativePixmapHandle backed GpuMemoryBufferHandle only.
+// !defined(USE_OZONE) so as to force GpuMemoryBufferSupport to select
+// gfx::ClientNativePixmapFactoryDmabuf for gfx::ClientNativePixmapFactory.
+#if defined(OS_LINUX) && !defined(USE_OZONE)
+TEST_F(VideoFrameStructTraitsTest, GpuMemoryBufferVideoFrame) {
+  gfx::Size coded_size = gfx::Size(256, 256);
+  gfx::Rect visible_rect(coded_size);
+  auto timestamp = base::TimeDelta::FromMilliseconds(1);
+  std::unique_ptr<gfx::GpuMemoryBuffer> gmb =
+      std::make_unique<FakeGpuMemoryBuffer>(
+          coded_size, gfx::BufferFormat::YUV_420_BIPLANAR);
+  gfx::BufferFormat expected_gmb_format = gmb->GetFormat();
+  gfx::Size expected_gmb_size = gmb->GetSize();
+  gpu::MailboxHolder mailbox_holders[media::VideoFrame::kMaxPlanes] = {
+      gpu::MailboxHolder(gpu::Mailbox::Generate(), gpu::SyncToken(), 5),
+      gpu::MailboxHolder(gpu::Mailbox::Generate(), gpu::SyncToken(), 10)};
+  auto frame = VideoFrame::WrapExternalGpuMemoryBuffer(
+      visible_rect, visible_rect.size(), std::move(gmb), mailbox_holders,
+      base::DoNothing::Once<const gpu::SyncToken&>(), timestamp);
+  ASSERT_TRUE(RoundTrip(&frame));
+  ASSERT_TRUE(frame);
+  ASSERT_EQ(frame->storage_type(), VideoFrame::STORAGE_GPU_MEMORY_BUFFER);
+  EXPECT_TRUE(frame->HasGpuMemoryBuffer());
+  EXPECT_FALSE(frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM));
+  EXPECT_EQ(frame->format(), PIXEL_FORMAT_NV12);
+  EXPECT_EQ(frame->coded_size(), coded_size);
+  EXPECT_EQ(frame->visible_rect(), visible_rect);
+  EXPECT_EQ(frame->natural_size(), visible_rect.size());
+  EXPECT_EQ(frame->timestamp(), timestamp);
+  ASSERT_TRUE(frame->HasTextures());
+  EXPECT_EQ(frame->mailbox_holder(0).mailbox, mailbox_holders[0].mailbox);
+  EXPECT_EQ(frame->mailbox_holder(1).mailbox, mailbox_holders[1].mailbox);
+  EXPECT_EQ(frame->GetGpuMemoryBuffer()->GetFormat(), expected_gmb_format);
+  EXPECT_EQ(frame->GetGpuMemoryBuffer()->GetSize(), expected_gmb_size);
+}
+#endif  // defined(OS_LINUX) && !defined(USE_OZONE)
 }  // namespace media
diff --git a/media/video/fake_gpu_memory_buffer.cc b/media/video/fake_gpu_memory_buffer.cc
index 76ede183..177365e 100644
--- a/media/video/fake_gpu_memory_buffer.cc
+++ b/media/video/fake_gpu_memory_buffer.cc
@@ -6,7 +6,7 @@
 
 #include "build/build_config.h"
 
-#if defined(OS_CHROMEOS)
+#if defined(OS_LINUX)
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -14,9 +14,9 @@
 
 namespace media {
 
-#if defined(OS_CHROMEOS)
+#if defined(OS_LINUX)
 base::ScopedFD GetDummyFD() {
-  base::ScopedFD fd(open("/dev/zero", O_RDONLY));
+  base::ScopedFD fd(open("/dev/zero", O_RDWR));
   DCHECK(fd.is_valid());
   return fd;
 }
@@ -37,7 +37,7 @@
   // Set a dummy id since this is for testing only.
   handle_.id = gfx::GpuMemoryBufferId(0);
 
-#if defined(OS_CHROMEOS)
+#if defined(OS_LINUX)
   // Set a dummy fd since this is for testing only.
   handle_.native_pixmap_handle.planes.push_back(
       gfx::NativePixmapPlane(size_.width(), 0, y_plane_size, GetDummyFD()));
@@ -46,7 +46,7 @@
         size_.width(), handle_.native_pixmap_handle.planes[0].size,
         uv_plane_size, GetDummyFD()));
   }
-#endif
+#endif  // defined(OS_LINUX)
 }
 
 FakeGpuMemoryBuffer::~FakeGpuMemoryBuffer() = default;
diff --git a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
index 05db39b..a484a2d 100644
--- a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
+++ b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
@@ -305,7 +305,7 @@
           log_message_body = value->message().c_str();
 
           interned_log_message_body =
-              interned_log_message_bodies_.LookupOrAdd(log_message_body);
+              interned_log_message_bodies_.LookupOrAdd(value->message());
         }  // else
       }    // else
       interned_source_location = interned_source_locations_.LookupOrAdd(
diff --git a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h
index c569967b..52a071a 100644
--- a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h
+++ b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h
@@ -74,7 +74,7 @@
   InterningIndex<const char*, std::string> interned_annotation_names_;
   InterningIndex<std::tuple<const char*, const char*, int>>
       interned_source_locations_;
-  InterningIndex<const char*> interned_log_message_bodies_;
+  InterningIndex<std::string> interned_log_message_bodies_;
 
   static std::atomic<uint32_t> incremental_state_reset_id_;
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index a74a666..2f90766 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2431,16 +2431,18 @@
             ]
         }
     ],
-    "ExploreSites InitialCountries": [
+    "ExploreSitesDense": [
         {
             "platforms": [
                 "android"
             ],
             "experiments": [
                 {
-                    "name": "WithGrouped_20190620",
+                    "name": "WithDenseBottom",
                     "params": {
-                        "mostLikelyVariation": "groupedIcon",
+                        "denseVariation": "titleBottom",
+                        "exp": "games-top",
+                        "mostLikelyvariation": "groupedIcon",
                         "variation": "mostLikelyTile"
                     },
                     "enable_features": [
@@ -2831,11 +2833,11 @@
                 {
                     "name": "Enabled",
                     "params": {
-                        "AllocationSamplingRange": "16",
-                        "MaxAllocations": "70",
-                        "MaxMetadata": "255",
+                        "AllocationSamplingRange": "64",
+                        "MaxAllocations": "35",
+                        "MaxMetadata": "150",
                         "ProcessSamplingBoost2": "10",
-                        "ProcessSamplingProbability": "0.05"
+                        "ProcessSamplingProbability": "0.01"
                     },
                     "enable_features": [
                         "GwpAsanMalloc",
@@ -2859,7 +2861,7 @@
                         "MaxAllocations": "70",
                         "MaxMetadata": "255",
                         "ProcessSamplingBoost2": "10",
-                        "ProcessSamplingProbability": "0.05"
+                        "ProcessSamplingProbability": "0.015"
                     },
                     "enable_features": [
                         "GwpAsanMalloc",
@@ -4392,7 +4394,8 @@
                 "linux",
                 "mac",
                 "windows",
-                "android"
+                "android",
+                "android_webview"
             ],
             "experiments": [
                 {
diff --git a/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc b/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
index 92cce9a2..fea69827 100644
--- a/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
+++ b/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
@@ -39,6 +39,8 @@
       return "document loaded";
     case WebSchedulerTrackedFeature::kDedicatedWorkerOrWorklet:
       return "Dedicated worker or worklet present";
+    case WebSchedulerTrackedFeature::kSharedWorker:
+      return "Shared worker present";
     case WebSchedulerTrackedFeature::kOutstandingNetworkRequest:
       return "outstanding network request";
     case WebSchedulerTrackedFeature::kServiceWorkerControlledPage:
diff --git a/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h b/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
index d865b97..53f27e4 100644
--- a/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
+++ b/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
@@ -69,8 +69,10 @@
   kWebVR = 30,
   kWebXR = 31,
 
+  kSharedWorker = 32,
+
   // NB: This enum is used in a bitmask, so kMaxValue must be less than 64.
-  kMaxValue = kWebXR
+  kMaxValue = kSharedWorker
 };
 
 static_assert(static_cast<uint32_t>(WebSchedulerTrackedFeature::kMaxValue) < 64,
diff --git a/third_party/blink/public/platform/web_localized_string.h b/third_party/blink/public/platform/web_localized_string.h
index f1e1bcd..643bbd7 100644
--- a/third_party/blink/public/platform/web_localized_string.h
+++ b/third_party/blink/public/platform/web_localized_string.h
@@ -37,39 +37,6 @@
   enum Name {
     kAXDayOfMonthFieldText,
     kAXHourFieldText,
-    kAXMediaAudioElement,
-    kAXMediaAudioElementHelp,
-    kAXMediaAudioSliderHelp,
-    kAXMediaCastOffButton,
-    kAXMediaCastOnButton,
-    kAXMediaCurrentTimeDisplay,
-    kAXMediaCurrentTimeDisplayHelp,
-    kAXMediaDefault,
-    kAXMediaDownloadButton,
-    kAXMediaEnterFullscreenButton,
-    kAXMediaExitFullscreenButton,
-    kAXMediaHideClosedCaptionsMenuButton,
-    kAXMediaMuteButton,
-    kAXMediaDisplayCutoutFullscreenButton,
-    kAXMediaLoadingPanel,
-    kAXMediaOverflowButton,
-    kAXMediaOverflowButtonHelp,
-    kAXMediaPauseButton,
-    kAXMediaPlayButton,
-    kAXMediaPlaybackError,
-    kAXMediaShowClosedCaptionsMenuButton,
-    kAXMediaTimeRemainingDisplay,
-    kAXMediaTimeRemainingDisplayHelp,
-    kAXMediaUnMuteButton,
-    kAXMediaVideoElement,
-    kAXMediaVideoElementHelp,
-    kAXMediaVideoSliderHelp,
-    kAXMediaVolumeSliderHelp,
-    kAXMediaEnterPictureInPictureButton,
-    kAXMediaExitPictureInPictureButton,
-    kAXMediaTouchLessPlayPauseAction,
-    kAXMediaTouchLessSeekAction,
-    kAXMediaTouchLessVolumeAction,
     kBlockedPluginText,
     kDetailsLabel,
     kInputElementAltText,
@@ -101,29 +68,9 @@
     kUnitsGibibytes,
     kUnitsTebibytes,
     kUnitsPebibytes,
-    kValidationBadInputForNumber,
-    kValidationBadInputForDateTime,
-    kValidationPatternMismatch,
-    kValidationRangeOverflowDateTime,
-    kValidationRangeUnderflowDateTime,
     kValidationStepMismatchCloseToLimit,
     kValidationTooShort,
     kValidationTooShortPlural,
-    kValidationTypeMismatch,
-    kValidationTypeMismatchForEmail,
-    kValidationTypeMismatchForEmailEmpty,
-    kValidationTypeMismatchForEmailEmptyDomain,
-    kValidationTypeMismatchForEmailEmptyLocal,
-    kValidationTypeMismatchForEmailInvalidDomain,
-    kValidationTypeMismatchForEmailInvalidDots,
-    kValidationTypeMismatchForEmailInvalidLocal,
-    kValidationTypeMismatchForEmailNoAtSign,
-    kValidationTypeMismatchForMultipleEmail,
-    kValidationTypeMismatchForURL,
-    kValidationValueMissingForCheckbox,
-    kValidationValueMissingForFile,
-    kValidationValueMissingForMultipleFile,
-    kValidationValueMissingForRadio,
     kWeekNumberLabel,
   };
 };
diff --git a/third_party/blink/public/web/web_embedded_worker.h b/third_party/blink/public/web/web_embedded_worker.h
index 66b856f98..aa3128c 100644
--- a/third_party/blink/public/web/web_embedded_worker.h
+++ b/third_party/blink/public/web/web_embedded_worker.h
@@ -45,7 +45,14 @@
 
 // As we're on the border line between non-Blink and Blink variants, we need
 // to use mojo::ScopedMessagePipeHandle to pass Mojo types.
-struct WebServiceWorkerInstalledScriptsManagerParams {
+struct BLINK_EXPORT WebServiceWorkerInstalledScriptsManagerParams {
+  WebServiceWorkerInstalledScriptsManagerParams() = delete;
+  WebServiceWorkerInstalledScriptsManagerParams(
+      WebVector<WebURL> installed_scripts_urls,
+      mojo::ScopedMessagePipeHandle manager_receiver,
+      mojo::ScopedMessagePipeHandle manager_host_remote);
+  ~WebServiceWorkerInstalledScriptsManagerParams() = default;
+
   WebVector<WebURL> installed_scripts_urls;
   // A handle for
   // mojo::PendingReceiver<mojom::blink::ServiceWorkerInstalledScriptsManager>.
@@ -63,10 +70,7 @@
   // WebServiceWorkerContextClient is owned by caller and must survive the
   // instance of WebEmbeddedWorker.
   static std::unique_ptr<WebEmbeddedWorker> Create(
-      WebServiceWorkerContextClient*,
-      mojo::ScopedMessagePipeHandle cache_storage,
-      mojo::ScopedMessagePipeHandle interface_provider,
-      mojo::ScopedMessagePipeHandle browser_interface_broker);
+      WebServiceWorkerContextClient*);
 
   virtual ~WebEmbeddedWorker() = default;
 
@@ -75,6 +79,9 @@
       std::unique_ptr<WebEmbeddedWorkerStartData>,
       std::unique_ptr<WebServiceWorkerInstalledScriptsManagerParams>,
       mojo::ScopedMessagePipeHandle content_settings_handle,
+      mojo::ScopedMessagePipeHandle cache_storage,
+      mojo::ScopedMessagePipeHandle interface_provider,
+      mojo::ScopedMessagePipeHandle browser_interface_broker,
       scoped_refptr<base::SingleThreadTaskRunner>
           initiator_thread_task_runner) = 0;
   virtual void TerminateWorkerContext() = 0;
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h
index 72e4a1d..1afcb7c 100644
--- a/third_party/blink/public/web/web_navigation_params.h
+++ b/third_party/blink/public/web/web_navigation_params.h
@@ -344,6 +344,10 @@
   WebVector<int> initiator_origin_trial_features;
 
   base::Optional<WebOriginPolicy> origin_policy;
+
+  // The base URL which will be set for the document to support relative path
+  // subresource loading in unsigned bundled exchanges file.
+  WebURL base_url_override_for_bundled_exchanges;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py b/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
index d51629a..370262a 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
@@ -484,7 +484,7 @@
             idl_type = factory.simple_type(
                 name='object', debug_info=debug_info)
             assert value_token == '{}'
-            value = object()
+            value = dict()
             literal = '{}'
         else:
             assert False, 'Unknown literal type: {}'.format(type_token)
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/literal_constant.py b/third_party/blink/renderer/bindings/scripts/web_idl/literal_constant.py
index d83cc94e..d3d948a4 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/literal_constant.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/literal_constant.py
@@ -47,7 +47,7 @@
         The values of the following literals are represented as follows.
         - null: None
         - []: list()
-        - {}: object()
+        - {}: dict()
         - true / false: True / False
         - INTEGER_NUMERICS: an instance of long
         - FLOATING_POINTS: an instance of float
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.cc b/third_party/blink/renderer/core/css/css_math_expression_node.cc
index 9be81b0..2c0595c 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.cc
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.cc
@@ -385,6 +385,9 @@
     const CSSMathExpressionNode* left_side,
     const CSSMathExpressionNode* right_side,
     CSSMathOperator op) {
+  if (left_side->IsMathFunction() || right_side->IsMathFunction())
+    return Create(left_side, right_side, op);
+
   CalculationCategory left_category = left_side->Category();
   CalculationCategory right_category = right_side->Category();
   DCHECK_NE(left_category, kCalcOther);
diff --git a/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc b/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc
index c852e49..b12614db1 100644
--- a/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/core/html/forms/base_temporal_input_type.h"
 
 #include <limits>
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/html/forms/chooser_only_temporal_input_type_view.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.h"
@@ -47,8 +48,7 @@
 static const int kMsecPerSecond = 1000;
 
 String BaseTemporalInputType::BadInputText() const {
-  return GetLocale().QueryString(
-      WebLocalizedString::kValidationBadInputForDateTime);
+  return GetLocale().QueryString(IDS_FORM_VALIDATION_BAD_INPUT_DATETIME);
 }
 
 InputTypeView* BaseTemporalInputType::CreateView() {
@@ -96,15 +96,13 @@
 }
 
 String BaseTemporalInputType::RangeOverflowText(const Decimal& maximum) const {
-  return GetLocale().QueryString(
-      WebLocalizedString::kValidationRangeOverflowDateTime,
-      LocalizeValue(Serialize(maximum)));
+  return GetLocale().QueryString(IDS_FORM_VALIDATION_RANGE_OVERFLOW_DATETIME,
+                                 LocalizeValue(Serialize(maximum)));
 }
 
 String BaseTemporalInputType::RangeUnderflowText(const Decimal& minimum) const {
-  return GetLocale().QueryString(
-      WebLocalizedString::kValidationRangeUnderflowDateTime,
-      LocalizeValue(Serialize(minimum)));
+  return GetLocale().QueryString(IDS_FORM_VALIDATION_RANGE_UNDERFLOW_DATETIME,
+                                 LocalizeValue(Serialize(minimum)));
 }
 
 Decimal BaseTemporalInputType::DefaultValueForStepUp() const {
diff --git a/third_party/blink/renderer/core/html/forms/checkbox_input_type.cc b/third_party/blink/renderer/core/html/forms/checkbox_input_type.cc
index 05bbb9dbc..8e67cb19 100644
--- a/third_party/blink/renderer/core/html/forms/checkbox_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/checkbox_input_type.cc
@@ -31,6 +31,7 @@
 
 #include "third_party/blink/renderer/core/html/forms/checkbox_input_type.h"
 
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/events/keyboard_event.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
@@ -54,8 +55,7 @@
 }
 
 String CheckboxInputType::ValueMissingText() const {
-  return GetLocale().QueryString(
-      WebLocalizedString::kValidationValueMissingForCheckbox);
+  return GetLocale().QueryString(IDS_FORM_VALIDATION_VALUE_MISSING_CHECKBOX);
 }
 
 void CheckboxInputType::HandleKeyupEvent(KeyboardEvent& event) {
diff --git a/third_party/blink/renderer/core/html/forms/email_input_type.cc b/third_party/blink/renderer/core/html/forms/email_input_type.cc
index fd9a06f..92767c9a 100644
--- a/third_party/blink/renderer/core/html/forms/email_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/email_input_type.cc
@@ -27,6 +27,7 @@
 #include <unicode/unistr.h>
 #include <unicode/uvernum.h>
 #include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_regexp.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
@@ -212,14 +213,15 @@
 String EmailInputType::TypeMismatchText() const {
   String invalid_address = FindInvalidAddress(GetElement().value());
   DCHECK(!invalid_address.IsNull());
-  if (invalid_address.IsEmpty())
+  if (invalid_address.IsEmpty()) {
     return GetLocale().QueryString(
-        WebLocalizedString::kValidationTypeMismatchForEmailEmpty);
+        IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_EMPTY);
+  }
   String at_sign = String("@");
   wtf_size_t at_index = invalid_address.find('@');
   if (at_index == kNotFound)
     return GetLocale().QueryString(
-        WebLocalizedString::kValidationTypeMismatchForEmailNoAtSign, at_sign,
+        IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_NO_AT_SIGN, at_sign,
         invalid_address);
   // We check validity against an ASCII value because of difficulty to check
   // invalid characters. However we should show Unicode value.
@@ -228,38 +230,38 @@
   String domain = invalid_address.Substring(at_index + 1);
   if (local_part.IsEmpty())
     return GetLocale().QueryString(
-        WebLocalizedString::kValidationTypeMismatchForEmailEmptyLocal, at_sign,
+        IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_EMPTY_LOCAL, at_sign,
         unicode_address);
   if (domain.IsEmpty())
     return GetLocale().QueryString(
-        WebLocalizedString::kValidationTypeMismatchForEmailEmptyDomain, at_sign,
+        IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_EMPTY_DOMAIN, at_sign,
         unicode_address);
   wtf_size_t invalid_char_index = local_part.Find(IsInvalidLocalPartCharacter);
   if (invalid_char_index != kNotFound) {
     unsigned char_length = U_IS_LEAD(local_part[invalid_char_index]) ? 2 : 1;
     return GetLocale().QueryString(
-        WebLocalizedString::kValidationTypeMismatchForEmailInvalidLocal,
-        at_sign, local_part.Substring(invalid_char_index, char_length));
+        IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_INVALID_LOCAL, at_sign,
+        local_part.Substring(invalid_char_index, char_length));
   }
   invalid_char_index = domain.Find(IsInvalidDomainCharacter);
   if (invalid_char_index != kNotFound) {
     unsigned char_length = U_IS_LEAD(domain[invalid_char_index]) ? 2 : 1;
     return GetLocale().QueryString(
-        WebLocalizedString::kValidationTypeMismatchForEmailInvalidDomain,
-        at_sign, domain.Substring(invalid_char_index, char_length));
+        IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_INVALID_DOMAIN, at_sign,
+        domain.Substring(invalid_char_index, char_length));
   }
   if (!CheckValidDotUsage(domain)) {
     wtf_size_t at_index_in_unicode = unicode_address.find('@');
     DCHECK_NE(at_index_in_unicode, kNotFound);
     return GetLocale().QueryString(
-        WebLocalizedString::kValidationTypeMismatchForEmailInvalidDots,
-        String("."), unicode_address.Substring(at_index_in_unicode + 1));
+        IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL_INVALID_DOTS, String("."),
+        unicode_address.Substring(at_index_in_unicode + 1));
   }
-  if (GetElement().Multiple())
+  if (GetElement().Multiple()) {
     return GetLocale().QueryString(
-        WebLocalizedString::kValidationTypeMismatchForMultipleEmail);
-  return GetLocale().QueryString(
-      WebLocalizedString::kValidationTypeMismatchForEmail);
+        IDS_FORM_VALIDATION_TYPE_MISMATCH_MULTIPLE_EMAIL);
+  }
+  return GetLocale().QueryString(IDS_FORM_VALIDATION_TYPE_MISMATCH_EMAIL);
 }
 
 bool EmailInputType::SupportsSelectionAPI() const {
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type.cc b/third_party/blink/renderer/core/html/forms/file_input_type.cc
index b971951..5abc22a 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/file_input_type.cc
@@ -148,9 +148,8 @@
 
 String FileInputType::ValueMissingText() const {
   return GetLocale().QueryString(
-      GetElement().Multiple()
-          ? WebLocalizedString::kValidationValueMissingForMultipleFile
-          : WebLocalizedString::kValidationValueMissingForFile);
+      GetElement().Multiple() ? IDS_FORM_VALIDATION_VALUE_MISSING_MULTIPLE_FILE
+                              : IDS_FORM_VALIDATION_VALUE_MISSING_FILE);
 }
 
 void FileInputType::HandleDOMActivateEvent(Event& event) {
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index f70d105..63a0e59 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -1707,12 +1707,18 @@
       event.SetDefaultHandled();
     } else if (is_multiple_ && key_code == ' ' &&
                IsSpatialNavigationEnabled(GetDocument().GetFrame())) {
-      // Use space to toggle selection change.
-      active_selection_state_ = !active_selection_state_;
-      UpdateSelectedState(active_selection_end_.Get(), true /*multi*/,
-                          false /*shift*/);
-      ListBoxOnChange();
-      event.SetDefaultHandled();
+      HTMLOptionElement* option = active_selection_end_;
+      // If there's no active selection,
+      // act as if "ArrowDown" had been pressed.
+      if (!option)
+        option = NextSelectableOption(LastSelectedOption());
+      if (option) {
+        // Use space to toggle selection change.
+        active_selection_state_ = !active_selection_state_;
+        UpdateSelectedState(option, true /*multi*/, false /*shift*/);
+        ListBoxOnChange();
+        event.SetDefaultHandled();
+      }
     }
   }
 }
diff --git a/third_party/blink/renderer/core/html/forms/input_type.cc b/third_party/blink/renderer/core/html/forms/input_type.cc
index 7482c28..bdce40be8 100644
--- a/third_party/blink/renderer/core/html/forms/input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/input_type.cc
@@ -378,7 +378,7 @@
 
 String InputType::BadInputText() const {
   NOTREACHED();
-  return GetLocale().QueryString(WebLocalizedString::kValidationTypeMismatch);
+  return GetLocale().QueryString(IDS_FORM_VALIDATION_TYPE_MISMATCH);
 }
 
 String InputType::RangeOverflowText(const Decimal&) const {
@@ -392,7 +392,7 @@
 }
 
 String InputType::TypeMismatchText() const {
-  return GetLocale().QueryString(WebLocalizedString::kValidationTypeMismatch);
+  return GetLocale().QueryString(IDS_FORM_VALIDATION_TYPE_MISMATCH);
 }
 
 String InputType::ValueMissingText() const {
@@ -421,7 +421,7 @@
     //   pattern. User agents may use the contents of this attribute, if it
     //   is present, when informing the user that the pattern is not matched
     return std::make_pair(
-        GetLocale().QueryString(WebLocalizedString::kValidationPatternMismatch),
+        GetLocale().QueryString(IDS_FORM_VALIDATION_PATTERN_MISMATCH),
         GetElement().FastGetAttribute(kTitleAttr).GetString());
   }
 
diff --git a/third_party/blink/renderer/core/html/forms/number_input_type.cc b/third_party/blink/renderer/core/html/forms/number_input_type.cc
index 7081c54..e49d1ca 100644
--- a/third_party/blink/renderer/core/html/forms/number_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/number_input_type.cc
@@ -262,8 +262,7 @@
 }
 
 String NumberInputType::BadInputText() const {
-  return GetLocale().QueryString(
-      WebLocalizedString::kValidationBadInputForNumber);
+  return GetLocale().QueryString(IDS_FORM_VALIDATION_BAD_INPUT_NUMBER);
 }
 
 String NumberInputType::RangeOverflowText(const Decimal& maximum) const {
diff --git a/third_party/blink/renderer/core/html/forms/radio_input_type.cc b/third_party/blink/renderer/core/html/forms/radio_input_type.cc
index f4f691d..01f83e4 100644
--- a/third_party/blink/renderer/core/html/forms/radio_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/radio_input_type.cc
@@ -21,6 +21,7 @@
 
 #include "third_party/blink/renderer/core/html/forms/radio_input_type.h"
 
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/events/keyboard_event.h"
@@ -60,8 +61,7 @@
 }
 
 String RadioInputType::ValueMissingText() const {
-  return GetLocale().QueryString(
-      WebLocalizedString::kValidationValueMissingForRadio);
+  return GetLocale().QueryString(IDS_FORM_VALIDATION_VALUE_MISSING_RADIO);
 }
 
 void RadioInputType::HandleClickEvent(MouseEvent& event) {
diff --git a/third_party/blink/renderer/core/html/forms/url_input_type.cc b/third_party/blink/renderer/core/html/forms/url_input_type.cc
index d672297..a442514 100644
--- a/third_party/blink/renderer/core/html/forms/url_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/url_input_type.cc
@@ -30,6 +30,7 @@
 
 #include "third_party/blink/renderer/core/html/forms/url_input_type.h"
 
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
@@ -55,8 +56,7 @@
 }
 
 String URLInputType::TypeMismatchText() const {
-  return GetLocale().QueryString(
-      WebLocalizedString::kValidationTypeMismatchForURL);
+  return GetLocale().QueryString(IDS_FORM_VALIDATION_TYPE_MISMATCH_URL);
 }
 
 String URLInputType::SanitizeValue(const String& proposed_value) const {
diff --git a/third_party/blink/renderer/core/html/media/video_wake_lock_test.cc b/third_party/blink/renderer/core/html/media/video_wake_lock_test.cc
index 148cbc4..3908425 100644
--- a/third_party/blink/renderer/core/html/media/video_wake_lock_test.cc
+++ b/third_party/blink/renderer/core/html/media/video_wake_lock_test.cc
@@ -9,6 +9,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/picture_in_picture/picture_in_picture.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/frame/picture_in_picture_controller.h"
@@ -86,16 +87,9 @@
 class VideoWakeLockFrameClient : public test::MediaStubLocalFrameClient {
  public:
   explicit VideoWakeLockFrameClient(std::unique_ptr<WebMediaPlayer> player)
-      : test::MediaStubLocalFrameClient(std::move(player)),
-        interface_provider_(new service_manager::InterfaceProvider()) {}
-
-  service_manager::InterfaceProvider* GetInterfaceProvider() override {
-    return interface_provider_.get();
-  }
+      : test::MediaStubLocalFrameClient(std::move(player)) {}
 
  private:
-  std::unique_ptr<service_manager::InterfaceProvider> interface_provider_;
-
   DISALLOW_COPY_AND_ASSIGN(VideoWakeLockFrameClient);
 };
 
@@ -106,9 +100,7 @@
         nullptr, MakeGarbageCollected<VideoWakeLockFrameClient>(
                      std::make_unique<VideoWakeLockMediaPlayer>()));
 
-    service_manager::InterfaceProvider::TestApi test_api(
-        GetFrame().Client()->GetInterfaceProvider());
-    test_api.SetBinderForName(
+    GetDocument().GetBrowserInterfaceBroker().SetBinderForTesting(
         mojom::blink::PictureInPictureService::Name_,
         WTF::BindRepeating(&VideoWakeLockPictureInPictureService::Bind,
                            WTF::Unretained(&pip_service_)));
@@ -120,6 +112,11 @@
     GetPage().SetIsHidden(false, true);
   }
 
+  void TearDown() override {
+    GetDocument().GetBrowserInterfaceBroker().SetBinderForTesting(
+        mojom::blink::PictureInPictureService::Name_, {});
+  }
+
   HTMLVideoElement* Video() const { return video_.Get(); }
   VideoWakeLock* GetVideoWakeLock() const { return video_wake_lock_.Get(); }
 
diff --git a/third_party/blink/renderer/core/layout/layout_theme.cc b/third_party/blink/renderer/core/layout/layout_theme.cc
index 71094a3d..a65978d 100644
--- a/third_party/blink/renderer/core/layout/layout_theme.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme.cc
@@ -770,7 +770,7 @@
     case CSSValueID::kLinktext:
       return 0xFF0000EE;
     case CSSValueID::kMenu:
-      return 0xFFC0C0C0;
+      return color_scheme == WebColorScheme::kDark ? 0xFF404040 : 0xFFC0C0C0;
     case CSSValueID::kMenutext:
       return color_scheme == WebColorScheme::kDark ? 0xFFFFFFFF : 0xFF000000;
     case CSSValueID::kScrollbar:
diff --git a/third_party/blink/renderer/core/layout/layout_theme_default.cc b/third_party/blink/renderer/core/layout/layout_theme_default.cc
index a27080f..f11b471 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_default.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_default.cc
@@ -72,6 +72,7 @@
   constexpr Color kDefaultButtonGrayColor(0xffdddddd);
   constexpr Color kDefaultButtonGrayColorDark(0xff444444);
   constexpr Color kDefaultMenuColor(0xfff7f7f7);
+  constexpr Color kDefaultMenuColorDark(0xff404040);
 
   if (css_value_id == CSSValueID::kButtonface) {
     if (UseMockTheme()) {
@@ -87,8 +88,14 @@
         return kDefaultButtonGrayColorDark;
     }
   }
-  if (css_value_id == CSSValueID::kMenu)
-    return kDefaultMenuColor;
+  if (css_value_id == CSSValueID::kMenu) {
+    switch (color_scheme) {
+      case WebColorScheme::kLight:
+        return kDefaultMenuColor;
+      case WebColorScheme::kDark:
+        return kDefaultMenuColorDark;
+    }
+  }
   return LayoutTheme::SystemColor(css_value_id, color_scheme);
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc b/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc
index e03cf0a..bd55b43 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc
@@ -47,6 +47,21 @@
   }
 };
 
+TEST_F(LayoutNGTextTest, SetTextWithOffsetAppendControl) {
+  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
+    return;
+
+  SetBodyInnerHTML(u"<pre id=target>a</pre>");
+  Text& text = To<Text>(*GetElementById("target")->firstChild());
+  // Note: "\n" is control character instead of text character.
+  text.appendData("\nX");
+
+  EXPECT_EQ(
+      "*{'a', ShapeResult=0+1}\n"
+      "*{'X', ShapeResult=2+1}\n",
+      GetItemsAsString(*text.GetLayoutObject()));
+}
+
 TEST_F(LayoutNGTextTest, SetTextWithOffsetAppendCollapseWhiteSpace) {
   if (!RuntimeEnabledFeatures::LayoutNGEnabled())
     return;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
index a27b698..70dfcb6 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
@@ -208,7 +208,6 @@
                return item.EndOffset() <= offset;
              });
          item != reusable_items_->end(); ++item) {
-      DCHECK_LE(start_offset, item->StartOffset());
       if (end_offset <= item->StartOffset())
         break;
       if (item->EndOffset() < start_offset)
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 ad471bb..ade17fa5 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
@@ -283,7 +283,7 @@
 
 // Initialize internal states for the next line.
 void NGLineBreaker::PrepareNextLine(NGLineInfo* line_info) {
-  // NGLineInfo is not supposed to be re-used becase it's not much gain and to
+  // NGLineInfo is not supposed to be re-used because it's not much gain and to
   // avoid rare code path.
   const NGInlineItemResults& item_results = line_info->Results();
   DCHECK(item_results.IsEmpty());
@@ -1642,6 +1642,7 @@
         // must not be at the end.
         LayoutUnit item_available_width =
             std::min(-width_to_rewind, item_result->inline_size - 1);
+        auto was_current_style = current_style_;
         SetCurrentStyle(*item.Style());
         BreakResult break_result =
             BreakText(item_result, item, *item.TextShapeResult(),
@@ -1672,6 +1673,8 @@
           }
           state_ = LineBreakState::kTrailing;
           return;
+        } else {
+          SetCurrentStyle(*was_current_style);
         }
         position_maybe_changed = true;
       }
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
index 9e47294..6f005867 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
@@ -34,6 +34,7 @@
 #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
 #include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
 #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h"
+#include "third_party/blink/renderer/core/layout/svg/transform_helper.h"
 #include "third_party/blink/renderer/core/layout/svg/transformed_hit_test_location.h"
 #include "third_party/blink/renderer/core/paint/image_element_timing.h"
 #include "third_party/blink/renderer/core/paint/svg_image_painter.h"
@@ -47,12 +48,20 @@
     : LayoutSVGModelObject(impl),
       needs_boundaries_update_(true),
       needs_transform_update_(true),
+      transform_uses_reference_box_(false),
       image_resource_(MakeGarbageCollected<LayoutImageResource>()) {
   image_resource_->Initialize(this);
 }
 
 LayoutSVGImage::~LayoutSVGImage() = default;
 
+void LayoutSVGImage::StyleDidChange(StyleDifference diff,
+                                    const ComputedStyle* old_style) {
+  transform_uses_reference_box_ =
+      TransformHelper::DependsOnReferenceBox(StyleRef());
+  LayoutSVGModelObject::StyleDidChange(diff, old_style);
+}
+
 void LayoutSVGImage::WillBeDestroyed() {
   image_resource_->Shutdown();
 
@@ -132,17 +141,11 @@
   if (EverHadLayout() && SelfNeedsLayout())
     SVGResourcesCache::ClientLayoutChanged(*this);
 
-  UpdateBoundingBox();
+  FloatPoint old_bbox_location = object_bounding_box_.Location();
+  bool bbox_changed = UpdateBoundingBox() ||
+                      old_bbox_location != object_bounding_box_.Location();
 
   bool update_parent_boundaries = false;
-  if (needs_transform_update_) {
-    local_transform_ =
-        ToSVGImageElement(GetElement())
-            ->CalculateTransform(SVGElement::kIncludeMotionTransform);
-    needs_transform_update_ = false;
-    update_parent_boundaries = true;
-  }
-
   if (needs_boundaries_update_) {
     local_visual_rect_ = object_bounding_box_;
     SVGLayoutSupport::AdjustVisualRectWithResources(*this, object_bounding_box_,
@@ -151,6 +154,18 @@
     update_parent_boundaries = true;
   }
 
+  if (!needs_transform_update_ && transform_uses_reference_box_) {
+    needs_transform_update_ = CheckForImplicitTransformChange(bbox_changed);
+    if (needs_transform_update_)
+      SetNeedsPaintPropertyUpdate();
+  }
+
+  if (needs_transform_update_) {
+    local_transform_ = CalculateLocalTransform();
+    needs_transform_update_ = false;
+    update_parent_boundaries = true;
+  }
+
   // If our bounds changed, notify the parents.
   if (update_parent_boundaries)
     LayoutSVGModelObject::SetNeedsBoundariesUpdate();
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_image.h b/third_party/blink/renderer/core/layout/svg/layout_svg_image.h
index b28f212..a56500d 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_image.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_image.h
@@ -57,6 +57,7 @@
   const char* GetName() const override { return "LayoutSVGImage"; }
 
  protected:
+  void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
   void WillBeDestroyed() override;
 
  private:
@@ -83,6 +84,7 @@
 
   bool needs_boundaries_update_ : 1;
   bool needs_transform_update_ : 1;
+  bool transform_uses_reference_box_ : 1;
   AffineTransform local_transform_;
   FloatRect object_bounding_box_;
   Persistent<LayoutImageResource> image_resource_;
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 46da80b..b4ca10c 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -271,6 +271,9 @@
     DCHECK(history_item);
     history_item_ = history_item;
   }
+
+  base_url_override_for_bundled_exchanges_ =
+      params_->base_url_override_for_bundled_exchanges;
 }
 
 FrameLoader& DocumentLoader::GetFrameLoader() const {
@@ -1269,10 +1272,12 @@
 
   ArchiveResource* main_resource =
       loading_mhtml_archive_ && archive_ ? archive_->MainResource() : nullptr;
-  if (main_resource)
+  if (main_resource) {
     FinishNavigationCommit(main_resource->MimeType(), main_resource->Url());
-  else
-    FinishNavigationCommit(response_.MimeType());
+  } else {
+    FinishNavigationCommit(response_.MimeType(),
+                           base_url_override_for_bundled_exchanges_);
+  }
 
   CHECK_GE(state_, kCommitted);
 
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index 04050ca..78b79b9 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -509,6 +509,7 @@
   WebScopedVirtualTimePauser virtual_time_pauser_;
   Member<SourceKeyedCachedMetadataHandler> cached_metadata_handler_;
   Member<PrefetchedSignedExchangeManager> prefetched_signed_exchange_manager_;
+  KURL base_url_override_for_bundled_exchanges_;
 
   // This UseCounterHelper tracks feature usage associated with the lifetime of
   // the document load. Features recorded prior to commit will be recorded
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc
index ddda6b7b..4c67799 100644
--- a/third_party/blink/renderer/core/loader/image_loader.cc
+++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -788,10 +788,6 @@
   image_complete_ = true;
   delay_until_image_notify_finished_ = nullptr;
 
-  // Update ImageAnimationPolicy for image_content_.
-  if (image_content_)
-    image_content_->UpdateImageAnimationPolicy();
-
   UpdateLayoutObject();
 
   if (image_content_ && image_content_->HasImage()) {
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
index 5f24567..6247d3f 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
@@ -148,15 +148,21 @@
   ImageObserver::Trace(visitor);
 }
 
-void ImageResourceContent::MarkObserverFinished(
+void ImageResourceContent::HandleObserverFinished(
     ImageResourceObserver* observer) {
-  ProhibitAddRemoveObserverInScope prohibit_add_remove_observer_in_scope(this);
-
-  auto it = observers_.find(observer);
-  if (it == observers_.end())
+  if (info_->SchedulingReloadOrShouldReloadBrokenPlaceholder())
     return;
-  observers_.erase(it);
-  finished_observers_.insert(observer);
+  {
+    ProhibitAddRemoveObserverInScope prohibit_add_remove_observer_in_scope(
+        this);
+    auto it = observers_.find(observer);
+    if (it != observers_.end()) {
+      observers_.erase(it);
+      finished_observers_.insert(observer);
+    }
+  }
+  observer->ImageNotifyFinished(this);
+  UpdateImageAnimationPolicy();
 }
 
 void ImageResourceContent::AddObserver(ImageResourceObserver* observer) {
@@ -177,11 +183,8 @@
     observer->ImageChanged(this, CanDeferInvalidation::kNo);
   }
 
-  if (IsLoaded() && observers_.Contains(observer) &&
-      !info_->SchedulingReloadOrShouldReloadBrokenPlaceholder()) {
-    MarkObserverFinished(observer);
-    observer->ImageNotifyFinished(this);
-  }
+  if (IsLoaded() && observers_.Contains(observer))
+    HandleObserverFinished(observer);
 }
 
 void ImageResourceContent::RemoveObserver(ImageResourceObserver* observer) {
@@ -296,10 +299,8 @@
       if (observers_.Contains(observer)) {
         observer->ImageChanged(this, defer);
         if (notifying_finish_option == kShouldNotifyFinish &&
-            observers_.Contains(observer) &&
-            !info_->SchedulingReloadOrShouldReloadBrokenPlaceholder()) {
-          MarkObserverFinished(observer);
-          observer->ImageNotifyFinished(this);
+            observers_.Contains(observer)) {
+          HandleObserverFinished(observer);
         }
       }
     }
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.h b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
index 0bfbbf9..21d1646 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_content.h
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
@@ -79,8 +79,6 @@
   IntSize IntrinsicSize(
       RespectImageOrientationEnum should_respect_image_orientation) const;
 
-  void UpdateImageAnimationPolicy();
-
   void AddObserver(ImageResourceObserver*);
   void RemoveObserver(ImageResourceObserver*);
 
@@ -202,10 +200,10 @@
 
   enum NotifyFinishOption { kShouldNotifyFinish, kDoNotNotifyFinish };
 
-  // If not null, changeRect is the changed part of the image.
   void NotifyObservers(NotifyFinishOption, CanDeferInvalidation);
-  void MarkObserverFinished(ImageResourceObserver*);
+  void HandleObserverFinished(ImageResourceObserver*);
   void UpdateToLoadedContentStatus(ResourceStatus);
+  void UpdateImageAnimationPolicy();
 
   class ProhibitAddRemoveObserverInScope : public base::AutoReset<bool> {
    public:
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
index c88c13e6..deb12e7 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/page/spatial_navigation_controller.h"
 
-#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/platform/web_scroll_into_view_params.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
@@ -641,7 +641,7 @@
     if (!frame)
       return spatial_navigation_host_;
 
-    frame->GetInterfaceProvider().GetInterface(
+    frame->GetBrowserInterfaceBroker().GetInterface(
         spatial_navigation_host_.BindNewPipeAndPassReceiver(
             frame->GetTaskRunner(TaskType::kMiscPlatformAPI)));
   }
diff --git a/third_party/blink/renderer/core/style/style_fetched_image.cc b/third_party/blink/renderer/core/style/style_fetched_image.cc
index e697847..abedb6b 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image.cc
+++ b/third_party/blink/renderer/core/style/style_fetched_image.cc
@@ -139,8 +139,6 @@
 
     if (document_ && image.IsSVGImage())
       ToSVGImage(image).UpdateUseCounters(*document_);
-
-    image_->UpdateImageAnimationPolicy();
   }
 
   if (document_ && RuntimeEnabledFeatures::ElementTimingEnabled(document_)) {
diff --git a/third_party/blink/renderer/core/workers/shared_worker.cc b/third_party/blink/renderer/core/workers/shared_worker.cc
index 24d0568c..55a4189 100644
--- a/third_party/blink/renderer/core/workers/shared_worker.cc
+++ b/third_party/blink/renderer/core/workers/shared_worker.cc
@@ -63,8 +63,12 @@
 
 }  // namespace
 
-inline SharedWorker::SharedWorker(ExecutionContext* context)
-    : AbstractWorker(context), is_being_connected_(false) {}
+SharedWorker::SharedWorker(ExecutionContext* context)
+    : AbstractWorker(context),
+      is_being_connected_(false),
+      feature_handle_for_scheduler_(context->GetScheduler()->RegisterFeature(
+          SchedulingPolicy::Feature::kSharedWorker,
+          {SchedulingPolicy::RecordMetricsForBackForwardCache()})) {}
 
 SharedWorker* SharedWorker::Create(ExecutionContext* context,
                                    const String& url,
diff --git a/third_party/blink/renderer/core/workers/shared_worker.h b/third_party/blink/renderer/core/workers/shared_worker.h
index f47d2a5e..75d7509 100644
--- a/third_party/blink/renderer/core/workers/shared_worker.h
+++ b/third_party/blink/renderer/core/workers/shared_worker.h
@@ -36,6 +36,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/workers/abstract_worker.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
 
 namespace blink {
@@ -73,6 +74,8 @@
  private:
   Member<MessagePort> port_;
   bool is_being_connected_;
+  FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle
+      feature_handle_for_scheduler_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_media_element.cc b/third_party/blink/renderer/modules/accessibility/ax_media_element.cc
index 93b3356..1d338be5 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_media_element.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_media_element.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/accessibility/ax_media_element.h"
 
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
@@ -35,8 +36,7 @@
   if (IsUnplayable()) {
     HTMLMediaElement* element =
         static_cast<HTMLMediaElement*>(layout_object_->GetNode());
-    return element->GetLocale().QueryString(
-        WebLocalizedString::kAXMediaPlaybackError);
+    return element->GetLocale().QueryString(IDS_MEDIA_PLAYBACK_ERROR);
   }
   return AXLayoutObject::TextAlternative(
       recursive, in_aria_labelled_by_traversal, visited, name_from,
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
index 91ea1cee..b35b45c 100644
--- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
+++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -70,35 +70,28 @@
 
 namespace blink {
 
+WebServiceWorkerInstalledScriptsManagerParams::
+    WebServiceWorkerInstalledScriptsManagerParams(
+        WebVector<WebURL> installed_scripts_urls,
+        mojo::ScopedMessagePipeHandle manager_receiver,
+        mojo::ScopedMessagePipeHandle manager_host_remote)
+    : installed_scripts_urls(std::move(installed_scripts_urls)),
+      manager_receiver(std::move(manager_receiver)),
+      manager_host_remote(std::move(manager_host_remote)) {
+  DCHECK(!this->installed_scripts_urls.empty());
+  DCHECK(this->manager_receiver);
+  DCHECK(this->manager_host_remote);
+}
+
 // static
 std::unique_ptr<WebEmbeddedWorker> WebEmbeddedWorker::Create(
-    WebServiceWorkerContextClient* client,
-    mojo::ScopedMessagePipeHandle cache_storage,
-    mojo::ScopedMessagePipeHandle interface_provider,
-    mojo::ScopedMessagePipeHandle browser_interface_broker) {
-  return std::make_unique<WebEmbeddedWorkerImpl>(
-      std::move(client),
-      mojo::PendingRemote<mojom::blink::CacheStorage>(
-          std::move(cache_storage), mojom::blink::CacheStorage::Version_),
-      service_manager::mojom::blink::InterfaceProviderPtrInfo(
-          std::move(interface_provider),
-          service_manager::mojom::blink::InterfaceProvider::Version_),
-      mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>(
-          std::move(browser_interface_broker),
-          mojom::blink::BrowserInterfaceBroker::Version_));
+    WebServiceWorkerContextClient* client) {
+  return std::make_unique<WebEmbeddedWorkerImpl>(std::move(client));
 }
 
 WebEmbeddedWorkerImpl::WebEmbeddedWorkerImpl(
-    WebServiceWorkerContextClient* client,
-    mojo::PendingRemote<mojom::blink::CacheStorage> cache_storage_remote,
-    service_manager::mojom::blink::InterfaceProviderPtrInfo
-        interface_provider_info,
-    mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>
-        browser_interface_broker)
-    : worker_context_client_(client),
-      cache_storage_remote_(std::move(cache_storage_remote)),
-      interface_provider_info_(std::move(interface_provider_info)),
-      browser_interface_broker_(std::move(browser_interface_broker)) {}
+    WebServiceWorkerContextClient* client)
+    : worker_context_client_(client) {}
 
 WebEmbeddedWorkerImpl::~WebEmbeddedWorkerImpl() {
   // TerminateWorkerContext() must be called before the destructor.
@@ -110,29 +103,19 @@
     std::unique_ptr<WebServiceWorkerInstalledScriptsManagerParams>
         installed_scripts_manager_params,
     mojo::ScopedMessagePipeHandle content_settings_handle,
+    mojo::ScopedMessagePipeHandle cache_storage,
+    mojo::ScopedMessagePipeHandle interface_provider,
+    mojo::ScopedMessagePipeHandle browser_interface_broker,
     scoped_refptr<base::SingleThreadTaskRunner> initiator_thread_task_runner) {
   DCHECK(!asked_to_terminate_);
 
   std::unique_ptr<ServiceWorkerInstalledScriptsManager>
       installed_scripts_manager;
   if (installed_scripts_manager_params) {
-    DCHECK(installed_scripts_manager_params->manager_receiver.is_valid());
-    DCHECK(installed_scripts_manager_params->manager_host_remote.is_valid());
-    Vector<KURL> installed_scripts_urls;
-    installed_scripts_urls.AppendRange(
-        installed_scripts_manager_params->installed_scripts_urls.begin(),
-        installed_scripts_manager_params->installed_scripts_urls.end());
-    installed_scripts_manager = std::make_unique<
-        ServiceWorkerInstalledScriptsManager>(
-        installed_scripts_urls,
-        mojo::PendingReceiver<
-            mojom::blink::ServiceWorkerInstalledScriptsManager>(
-            std::move(installed_scripts_manager_params->manager_receiver)),
-        mojo::PendingRemote<
-            mojom::blink::ServiceWorkerInstalledScriptsManagerHost>(
-            std::move(installed_scripts_manager_params->manager_host_remote),
-            mojom::blink::ServiceWorkerInstalledScriptsManagerHost::Version_),
-        Platform::Current()->GetIOTaskRunner());
+    installed_scripts_manager =
+        std::make_unique<ServiceWorkerInstalledScriptsManager>(
+            std::move(installed_scripts_manager_params),
+            Platform::Current()->GetIOTaskRunner());
   }
 
   // TODO(mkwst): This really needs to be piped through from the requesting
@@ -157,6 +140,14 @@
           // TODO(falken): Is that comment about versioning correct?
           mojo::PendingRemote<mojom::blink::WorkerContentSettingsProxy>(
               std::move(content_settings_handle), 0u)),
+      mojo::PendingRemote<mojom::blink::CacheStorage>(
+          std::move(cache_storage), mojom::blink::CacheStorage::Version_),
+      service_manager::mojom::blink::InterfaceProviderPtrInfo(
+          std::move(interface_provider),
+          service_manager::mojom::blink::InterfaceProvider::Version_),
+      mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>(
+          std::move(browser_interface_broker),
+          mojom::blink::BrowserInterfaceBroker::Version_),
       std::move(initiator_thread_task_runner));
 }
 
@@ -179,6 +170,11 @@
     std::unique_ptr<ServiceWorkerInstalledScriptsManager>
         installed_scripts_manager,
     std::unique_ptr<ServiceWorkerContentSettingsProxy> content_settings_proxy,
+    mojo::PendingRemote<mojom::blink::CacheStorage> cache_storage_remote,
+    service_manager::mojom::blink::InterfaceProviderPtrInfo
+        interface_provider_info,
+    mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>
+        browser_interface_broker,
     scoped_refptr<base::SingleThreadTaskRunner> initiator_thread_task_runner) {
   DCHECK(!asked_to_terminate_);
 
@@ -219,10 +215,6 @@
   String source_code;
   std::unique_ptr<Vector<uint8_t>> cached_meta_data;
 
-  bool is_script_installed =
-      installed_scripts_manager && installed_scripts_manager->IsScriptInstalled(
-                                       worker_start_data->script_url);
-
   // We don't have to set ContentSecurityPolicy and ReferrerPolicy. They're
   // served by the worker script loader or the installed scripts manager on the
   // worker thread.
@@ -238,7 +230,7 @@
       std::move(worker_settings),
       static_cast<V8CacheOptions>(worker_start_data->v8_cache_options),
       nullptr /* worklet_module_respones_map */,
-      std::move(interface_provider_info_), std::move(browser_interface_broker_),
+      std::move(interface_provider_info), std::move(browser_interface_broker),
       BeginFrameProviderParams(), nullptr /* parent_feature_policy */,
       base::UnguessableToken() /* agent_cluster_id */);
 
@@ -249,7 +241,7 @@
   worker_thread_ = std::make_unique<ServiceWorkerThread>(
       std::make_unique<ServiceWorkerGlobalScopeProxy>(
           *this, *worker_context_client_, initiator_thread_task_runner),
-      std::move(installed_scripts_manager), std::move(cache_storage_remote_),
+      std::move(installed_scripts_manager), std::move(cache_storage_remote),
       initiator_thread_task_runner);
 
   auto devtools_params = std::make_unique<WorkerDevToolsParams>();
@@ -269,58 +261,38 @@
                         WorkerBackingThreadStartupData::CreateDefault(),
                         std::move(devtools_params));
 
-  // If this is an installed service worker, the installed script will be read
-  // from the service worker script storage on the worker thread.
-  if (is_script_installed) {
-    switch (worker_start_data->script_type) {
-      case mojom::ScriptType::kClassic:
-        worker_thread_->RunInstalledClassicScript(
-            worker_start_data->script_url, v8_inspector::V8StackTraceId());
-        break;
-      case mojom::ScriptType::kModule:
-        worker_thread_->RunInstalledModuleScript(
-            worker_start_data->script_url,
-            CreateFetchClientSettingsObjectData(
-                worker_start_data->script_url, starter_origin.get(),
-                starter_https_state, worker_start_data->address_space),
-            network::mojom::CredentialsMode::kOmit);
-        break;
-    }
-  } else {
-    std::unique_ptr<CrossThreadFetchClientSettingsObjectData>
-        fetch_client_setting_object_data = CreateFetchClientSettingsObjectData(
-            worker_start_data->script_url, starter_origin.get(),
-            starter_https_state, worker_start_data->address_space);
+  std::unique_ptr<CrossThreadFetchClientSettingsObjectData>
+      fetch_client_setting_object_data = CreateFetchClientSettingsObjectData(
+          worker_start_data->script_url, starter_origin.get(),
+          starter_https_state, worker_start_data->address_space);
 
-    // If this is a new (not installed) service worker, we are in the Update
-    // algorithm here:
-    // > Switching on job's worker type, run these substeps with the following
-    // > options:
-    // https://w3c.github.io/ServiceWorker/#update-algorithm
-    switch (worker_start_data->script_type) {
-      // > "classic": Fetch a classic worker script given job's serialized
-      // > script url, job's client, "serviceworker", and the to-be-created
-      // > environment settings object for this service worker.
-      case mojom::ScriptType::kClassic:
-        worker_thread_->FetchAndRunClassicScript(
-            worker_start_data->script_url,
-            std::move(fetch_client_setting_object_data),
-            nullptr /* outside_resource_timing_notifier */,
-            v8_inspector::V8StackTraceId());
-        break;
+  // > Switching on job's worker type, run these substeps with the following
+  // > options:
+  // https://w3c.github.io/ServiceWorker/#update-algorithm
+  switch (worker_start_data->script_type) {
+    // > "classic": Fetch a classic worker script given job's serialized script
+    // > url, job's client, "serviceworker", and the to-be-created environment
+    // > settings object for this service worker.
+    case mojom::ScriptType::kClassic:
+      worker_thread_->FetchAndRunClassicScript(
+          worker_start_data->script_url,
+          std::move(fetch_client_setting_object_data),
+          nullptr /* outside_resource_timing_notifier */,
+          v8_inspector::V8StackTraceId());
+      break;
 
-      // > "module": Fetch a module worker script graph given job’s serialized
-      // > script url, job’s client, "serviceworker", "omit", and the
-      // > to-be-created environment settings object for this service worker.
-      case mojom::ScriptType::kModule:
-        worker_thread_->FetchAndRunModuleScript(
-            worker_start_data->script_url,
-            std::move(fetch_client_setting_object_data),
-            nullptr /* outside_resource_timing_notifier */,
-            network::mojom::CredentialsMode::kOmit);
-        break;
-    }
+    // > "module": Fetch a module worker script graph given job’s serialized
+    // > script url, job’s client, "serviceworker", "omit", and the
+    // > to-be-created environment settings object for this service worker.
+    case mojom::ScriptType::kModule:
+      worker_thread_->FetchAndRunModuleScript(
+          worker_start_data->script_url,
+          std::move(fetch_client_setting_object_data),
+          nullptr /* outside_resource_timing_notifier */,
+          network::mojom::CredentialsMode::kOmit);
+      break;
   }
+
   // We are now ready to inspect worker thread.
   worker_context_client_->WorkerReadyForInspectionOnInitiatorThread(
       devtools_agent_remote.PassPipe(),
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h
index 0dd19b0..b6bec07 100644
--- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h
+++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h
@@ -61,11 +61,7 @@
 // worker startup. See https://crbug.com/988335 for details.
 class MODULES_EXPORT WebEmbeddedWorkerImpl final : public WebEmbeddedWorker {
  public:
-  WebEmbeddedWorkerImpl(
-      WebServiceWorkerContextClient*,
-      mojo::PendingRemote<mojom::blink::CacheStorage>,
-      service_manager::mojom::blink::InterfaceProviderPtrInfo,
-      mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>);
+  explicit WebEmbeddedWorkerImpl(WebServiceWorkerContextClient*);
   ~WebEmbeddedWorkerImpl() override;
 
   // WebEmbeddedWorker overrides.
@@ -73,6 +69,9 @@
       std::unique_ptr<WebEmbeddedWorkerStartData>,
       std::unique_ptr<WebServiceWorkerInstalledScriptsManagerParams>,
       mojo::ScopedMessagePipeHandle content_settings_handle,
+      mojo::ScopedMessagePipeHandle cache_storage,
+      mojo::ScopedMessagePipeHandle interface_provider,
+      mojo::ScopedMessagePipeHandle browser_interface_broker,
       scoped_refptr<base::SingleThreadTaskRunner> initiator_thread_task_runner)
       override;
   void TerminateWorkerContext() override;
@@ -85,6 +84,9 @@
       std::unique_ptr<WebEmbeddedWorkerStartData> worker_start_data,
       std::unique_ptr<ServiceWorkerInstalledScriptsManager>,
       std::unique_ptr<ServiceWorkerContentSettingsProxy>,
+      mojo::PendingRemote<mojom::blink::CacheStorage>,
+      service_manager::mojom::blink::InterfaceProviderPtrInfo,
+      mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>,
       scoped_refptr<base::SingleThreadTaskRunner> initiator_thread_task_runner);
 
   // Creates a cross-thread copyable outside settings object for top-level
@@ -102,14 +104,6 @@
 
   bool asked_to_terminate_ = false;
 
-  mojo::PendingRemote<mojom::blink::CacheStorage> cache_storage_remote_;
-
-  service_manager::mojom::blink::InterfaceProviderPtrInfo
-      interface_provider_info_;
-
-  mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>
-      browser_interface_broker_;
-
   DISALLOW_COPY_AND_ASSIGN(WebEmbeddedWorkerImpl);
 };
 
diff --git a/third_party/blink/renderer/modules/keyboard/keyboard_layout.cc b/third_party/blink/renderer/modules/keyboard/keyboard_layout.cc
index 7e1e981..f787a31 100644
--- a/third_party/blink/renderer/modules/keyboard/keyboard_layout.cc
+++ b/third_party/blink/renderer/modules/keyboard/keyboard_layout.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/modules/keyboard/keyboard_layout.h"
 
-#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
@@ -80,7 +80,7 @@
     if (!frame) {
       return false;
     }
-    frame->GetInterfaceProvider().GetInterface(
+    frame->GetBrowserInterfaceBroker().GetInterface(
         service_.BindNewPipeAndPassReceiver());
     DCHECK(service_);
   }
diff --git a/third_party/blink/renderer/modules/keyboard/keyboard_lock.cc b/third_party/blink/renderer/modules/keyboard/keyboard_lock.cc
index 198c20c..575c8b8 100644
--- a/third_party/blink/renderer/modules/keyboard/keyboard_lock.cc
+++ b/third_party/blink/renderer/modules/keyboard/keyboard_lock.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/modules/keyboard/keyboard_lock.h"
 
-#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
@@ -100,7 +100,7 @@
       return false;
     }
     // See https://bit.ly/2S0zRAS for task types.
-    frame->GetInterfaceProvider().GetInterface(
+    frame->GetBrowserInterfaceBroker().GetInterface(
         service_.BindNewPipeAndPassReceiver(
             frame->GetTaskRunner(TaskType::kMiscPlatformAPI)));
     DCHECK(service_);
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_cast_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_cast_button_element.cc
index d254f259..03536cd 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_cast_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_cast_button_element.cc
@@ -57,12 +57,12 @@
 void MediaControlCastButtonElement::UpdateDisplayType() {
   if (IsPlayingRemotely()) {
     setAttribute(html_names::kAriaLabelAttr,
-                 WTF::AtomicString(GetLocale().QueryString(
-                     WebLocalizedString::kAXMediaCastOnButton)));
+                 WTF::AtomicString(
+                     GetLocale().QueryString(IDS_AX_MEDIA_CAST_ON_BUTTON)));
   } else {
     setAttribute(html_names::kAriaLabelAttr,
-                 WTF::AtomicString(GetLocale().QueryString(
-                     WebLocalizedString::kAXMediaCastOffButton)));
+                 WTF::AtomicString(
+                     GetLocale().QueryString(IDS_AX_MEDIA_CAST_OFF_BUTTON)));
   }
   UpdateOverflowString();
   SetClass("on", IsPlayingRemotely());
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_current_time_display_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_current_time_display_element.cc
index 92d077b7..42baaa4 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_current_time_display_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_current_time_display_element.cc
@@ -4,15 +4,15 @@
 
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_current_time_display_element.h"
 
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/modules/media_controls/media_controls_impl.h"
 
 namespace blink {
 
 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(
     MediaControlsImpl& media_controls)
-    : MediaControlTimeDisplayElement(
-          media_controls,
-          WebLocalizedString::kAXMediaCurrentTimeDisplay) {
+    : MediaControlTimeDisplayElement(media_controls,
+                                     IDS_AX_MEDIA_CURRENT_TIME_DISPLAY) {
   SetShadowPseudoId(
       AtomicString("-webkit-media-controls-current-time-display"));
 }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_display_cutout_fullscreen_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_display_cutout_fullscreen_button_element.cc
index 80c544b9..5ed7487 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_display_cutout_fullscreen_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_display_cutout_fullscreen_button_element.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_display_cutout_fullscreen_button_element.h"
 
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/frame/viewport_data.h"
@@ -21,7 +22,7 @@
   setType(input_type_names::kButton);
   setAttribute(html_names::kAriaLabelAttr,
                WTF::AtomicString(GetLocale().QueryString(
-                   WebLocalizedString::kAXMediaDisplayCutoutFullscreenButton)));
+                   IDS_AX_MEDIA_DISPLAY_CUT_OUT_FULL_SCREEN_BUTTON)));
   SetShadowPseudoId(AtomicString(
       "-internal-media-controls-display-cutout-fullscreen-button"));
   SetIsWanted(false);
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_display_cutout_fullscreen_button_element_test.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_display_cutout_fullscreen_button_element_test.cc
index e7cf746..eb065bd 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_display_cutout_fullscreen_button_element_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_display_cutout_fullscreen_button_element_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_display_cutout_fullscreen_button_element.h"
 
 #include "third_party/blink/public/mojom/page/display_cutout.mojom-blink.h"
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/events/touch_event.h"
 #include "third_party/blink/renderer/core/frame/viewport_data.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
@@ -98,7 +99,7 @@
 TEST_F(MediaControlDisplayCutoutFullscreenButtonElementTest,
        Fullscreen_ButtonAccessibility) {
   EXPECT_EQ(display_cutout_fullscreen_button_->GetLocale().QueryString(
-                WebLocalizedString::kAXMediaDisplayCutoutFullscreenButton),
+                IDS_AX_MEDIA_DISPLAY_CUT_OUT_FULL_SCREEN_BUTTON),
             display_cutout_fullscreen_button_->getAttribute(
                 html_names::kAriaLabelAttr));
 }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc
index 0fe04ab..934b99f 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc
@@ -23,9 +23,9 @@
     MediaControlsImpl& media_controls)
     : MediaControlInputElement(media_controls) {
   setType(input_type_names::kButton);
-  setAttribute(html_names::kAriaLabelAttr,
-               WTF::AtomicString(GetLocale().QueryString(
-                   WebLocalizedString::kAXMediaDownloadButton)));
+  setAttribute(
+      html_names::kAriaLabelAttr,
+      WTF::AtomicString(GetLocale().QueryString(IDS_AX_MEDIA_DOWNLOAD_BUTTON)));
 
   SetShadowPseudoId(AtomicString("-internal-media-controls-download-button"));
   SetIsWanted(false);
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_fullscreen_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_fullscreen_button_element.cc
index d241534..efcd592 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_fullscreen_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_fullscreen_button_element.cc
@@ -29,11 +29,11 @@
   if (is_fullscreen) {
     setAttribute(html_names::kAriaLabelAttr,
                  WTF::AtomicString(GetLocale().QueryString(
-                     WebLocalizedString::kAXMediaExitFullscreenButton)));
+                     IDS_AX_MEDIA_EXIT_FULL_SCREEN_BUTTON)));
   } else {
     setAttribute(html_names::kAriaLabelAttr,
                  WTF::AtomicString(GetLocale().QueryString(
-                     WebLocalizedString::kAXMediaEnterFullscreenButton)));
+                     IDS_AX_MEDIA_ENTER_FULL_SCREEN_BUTTON)));
   }
   SetClass("fullscreen", is_fullscreen);
 }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.cc
index a753f9a..8950bbe 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.h"
 
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/css/css_style_declaration.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/events/event_listener.h"
@@ -38,9 +39,9 @@
     MediaControlsImpl& media_controls)
     : MediaControlDivElement(media_controls) {
   SetShadowPseudoId(AtomicString("-internal-media-controls-loading-panel"));
-  setAttribute(html_names::kAriaLabelAttr,
-               WTF::AtomicString(GetLocale().QueryString(
-                   WebLocalizedString::kAXMediaLoadingPanel)));
+  setAttribute(
+      html_names::kAriaLabelAttr,
+      WTF::AtomicString(GetLocale().QueryString(IDS_AX_MEDIA_LOADING_PANEL)));
   setAttribute(html_names::kAriaLiveAttr, "polite");
   CreateUserAgentShadowRoot();
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_mute_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_mute_button_element.cc
index 91c6ead..2ed62bc 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_mute_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_mute_button_element.cc
@@ -30,10 +30,10 @@
   // 'muted' when the volume is 0 even if the element is not muted. This allows
   // the painting and the display type to actually match.
   bool muted = MediaElement().muted() || MediaElement().volume() == 0;
-  setAttribute(html_names::kAriaLabelAttr,
-               WTF::AtomicString(GetLocale().QueryString(
-                   muted ? WebLocalizedString::kAXMediaUnMuteButton
-                         : WebLocalizedString::kAXMediaMuteButton)));
+  setAttribute(
+      html_names::kAriaLabelAttr,
+      WTF::AtomicString(GetLocale().QueryString(
+          muted ? IDS_AX_MEDIA_UNMUTE_BUTTON : IDS_AX_MEDIA_MUTE_BUTTON)));
   SetClass("muted", muted);
   UpdateOverflowString();
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_overflow_menu_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_overflow_menu_button_element.cc
index 284eaf03..3fce3d9 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_overflow_menu_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_overflow_menu_button_element.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_overflow_menu_button_element.h"
 
 #include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
 #include "third_party/blink/renderer/modules/media_controls/media_controls_impl.h"
@@ -16,12 +17,12 @@
     MediaControlsImpl& media_controls)
     : MediaControlInputElement(media_controls) {
   setType(input_type_names::kButton);
-  setAttribute(html_names::kAriaLabelAttr,
-               WTF::AtomicString(GetLocale().QueryString(
-                   WebLocalizedString::kAXMediaOverflowButton)));
+  setAttribute(
+      html_names::kAriaLabelAttr,
+      WTF::AtomicString(GetLocale().QueryString(IDS_AX_MEDIA_OVERFLOW_BUTTON)));
   setAttribute(html_names::kTitleAttr,
-               WTF::AtomicString(GetLocale().QueryString(
-                   WebLocalizedString::kAXMediaOverflowButtonHelp)));
+               WTF::AtomicString(
+                   GetLocale().QueryString(IDS_AX_MEDIA_OVERFLOW_BUTTON_HELP)));
   SetShadowPseudoId(AtomicString("-internal-media-controls-overflow-button"));
   SetIsWanted(false);
 }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
index e41057b..a06074c 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
@@ -6,6 +6,7 @@
 
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
@@ -47,9 +48,8 @@
 void MediaControlOverlayPlayButtonElement::UpdateDisplayType() {
   SetIsWanted(MediaElement().ShouldShowControls());
 
-  WebLocalizedString::Name state =
-      MediaElement().paused() ? WebLocalizedString::kAXMediaPlayButton
-                              : WebLocalizedString::kAXMediaPauseButton;
+  int state = MediaElement().paused() ? IDS_AX_MEDIA_PLAY_BUTTON
+                                      : IDS_AX_MEDIA_PAUSE_BUTTON;
   setAttribute(html_names::kAriaLabelAttr,
                WTF::AtomicString(GetLocale().QueryString(state)));
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_picture_in_picture_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_picture_in_picture_button_element.cc
index c33f481f..333982b 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_picture_in_picture_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_picture_in_picture_button_element.cc
@@ -102,10 +102,9 @@
     bool isInPictureInPicture) {
   String aria_string =
       isInPictureInPicture
-          ? GetLocale().QueryString(
-                WebLocalizedString::kAXMediaExitPictureInPictureButton)
+          ? GetLocale().QueryString(IDS_AX_MEDIA_EXIT_PICTURE_IN_PICTURE_BUTTON)
           : GetLocale().QueryString(
-                WebLocalizedString::kAXMediaEnterPictureInPictureButton);
+                IDS_AX_MEDIA_ENTER_PICTURE_IN_PICTURE_BUTTON);
 
   setAttribute(html_names::kAriaLabelAttr, WTF::AtomicString(aria_string));
 }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_play_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_play_button_element.cc
index 27001b8..526d64f 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_play_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_play_button_element.cc
@@ -26,9 +26,8 @@
 }
 
 void MediaControlPlayButtonElement::UpdateDisplayType() {
-  WebLocalizedString::Name state =
-      MediaElement().paused() ? WebLocalizedString::kAXMediaPlayButton
-                              : WebLocalizedString::kAXMediaPauseButton;
+  int state = MediaElement().paused() ? IDS_AX_MEDIA_PLAY_BUTTON
+                                      : IDS_AX_MEDIA_PAUSE_BUTTON;
   setAttribute(html_names::kAriaLabelAttr,
                WTF::AtomicString(GetLocale().QueryString(state)));
   SetClass("pause", MediaElement().paused());
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_remaining_time_display_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_remaining_time_display_element.cc
index 1622c84..d5fa0869 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_remaining_time_display_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_remaining_time_display_element.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_remaining_time_display_element.h"
 
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/modules/media_controls/media_controls_impl.h"
 
 namespace {
@@ -18,9 +19,8 @@
 
 MediaControlRemainingTimeDisplayElement::
     MediaControlRemainingTimeDisplayElement(MediaControlsImpl& media_controls)
-    : MediaControlTimeDisplayElement(
-          media_controls,
-          WebLocalizedString::kAXMediaTimeRemainingDisplay) {
+    : MediaControlTimeDisplayElement(media_controls,
+                                     IDS_AX_MEDIA_TIME_REMAINING_DISPLAY) {
   SetShadowPseudoId(
       AtomicString("-webkit-media-controls-time-remaining-display"));
 }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc
index 55959a5..859dc94 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.h"
 
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
 #include "third_party/blink/renderer/core/dom/text.h"
@@ -182,7 +183,7 @@
   header_item->setAttribute(
       html_names::kAriaLabelAttr,
       AtomicString(GetLocale().QueryString(
-          WebLocalizedString::kAXMediaHideClosedCaptionsMenuButton)));
+          IDS_AX_MEDIA_HIDE_CLOSED_CAPTIONS_MENU_BUTTON)));
   header_item->setTabIndex(0);
   return header_item;
 }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_time_display_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_time_display_element.cc
index e8e525b..c1e60fc 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_time_display_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_time_display_element.cc
@@ -25,9 +25,9 @@
 
 MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(
     MediaControlsImpl& media_controls,
-    blink::WebLocalizedString::Name localized_label)
+    int localized_resource_id)
     : MediaControlDivElement(media_controls),
-      localized_label_(localized_label) {
+      localized_resource_id_(localized_resource_id) {
   SetAriaLabel();
 }
 
@@ -58,7 +58,8 @@
 }
 
 void MediaControlTimeDisplayElement::SetAriaLabel() {
-  String aria_label = GetLocale().QueryString(localized_label_, FormatTime());
+  String aria_label =
+      GetLocale().QueryString(localized_resource_id_, FormatTime());
   setAttribute(html_names::kAriaLabelAttr, AtomicString(aria_label));
 }
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_time_display_element.h b/third_party/blink/renderer/modules/media_controls/elements/media_control_time_display_element.h
index 2176a86..661fef4 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_time_display_element.h
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_time_display_element.h
@@ -21,8 +21,7 @@
   WebSize GetSizeOrDefault() const override;
 
  protected:
-  MediaControlTimeDisplayElement(MediaControlsImpl&,
-                                 blink::WebLocalizedString::Name);
+  MediaControlTimeDisplayElement(MediaControlsImpl&, int resource_id);
 
   virtual int EstimateElementWidth() const;
 
@@ -32,7 +31,7 @@
   void SetAriaLabel();
 
   double current_value_ = 0;
-  blink::WebLocalizedString::Name localized_label_;
+  int localized_resource_id_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc
index 946a9fd..ffb6a06 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc
@@ -6,6 +6,7 @@
 
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_screen_info.h"
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/events/keyboard_event.h"
@@ -66,17 +67,16 @@
 void MediaControlTimelineElement::SetPosition(double current_time) {
   setValue(String::Number(current_time));
   String aria_label =
-      GetLocale().QueryString(
-          MediaElement().IsHTMLVideoElement()
-              ? WebLocalizedString::kAXMediaVideoSliderHelp
-              : WebLocalizedString::kAXMediaAudioSliderHelp) +
+      GetLocale().QueryString(MediaElement().IsHTMLVideoElement()
+                                  ? IDS_AX_MEDIA_VIDEO_SLIDER_HELP
+                                  : IDS_AX_MEDIA_AUDIO_SLIDER_HELP) +
       " " + GetMediaControls().CurrentTimeDisplay().textContent(true) + " " +
       GetMediaControls().RemainingTimeDisplay().textContent(true);
   setAttribute(html_names::kAriaLabelAttr, AtomicString(aria_label));
 
   setAttribute(html_names::kAriaValuetextAttr,
                AtomicString(GetLocale().QueryString(
-                   WebLocalizedString::kAXMediaCurrentTimeDisplay,
+                   IDS_AX_MEDIA_CURRENT_TIME_DISPLAY,
                    GetMediaControls().CurrentTimeDisplay().textContent(true))));
   RenderBarSegments();
 }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_toggle_closed_captions_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_toggle_closed_captions_button_element.cc
index f199c42..53cb32f 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_toggle_closed_captions_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_toggle_closed_captions_button_element.cc
@@ -51,7 +51,7 @@
     : MediaControlInputElement(media_controls) {
   setAttribute(html_names::kAriaLabelAttr,
                WTF::AtomicString(GetLocale().QueryString(
-                   WebLocalizedString::kAXMediaShowClosedCaptionsMenuButton)));
+                   IDS_AX_MEDIA_SHOW_CLOSED_CAPTIONS_MENU_BUTTON)));
   setType(input_type_names::kButton);
   SetShadowPseudoId(
       AtomicString("-webkit-media-controls-toggle-closed-captions-button"));
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_overlay_element.cc b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_overlay_element.cc
index 7ae6f42..75e0b36 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_overlay_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_overlay_element.cc
@@ -43,14 +43,14 @@
   ParserAppendChild(volume_down_button);
 
   StringBuilder aria_label;
-  aria_label.Append(GetLocale().QueryString(
-      WebLocalizedString::kAXMediaTouchLessPlayPauseAction));
+  aria_label.Append(
+      GetLocale().QueryString(IDS_AX_MEDIA_TOUCHLESS_PLAY_PAUSE_ACTION));
   aria_label.Append(" ");
   aria_label.Append(
-      GetLocale().QueryString(WebLocalizedString::kAXMediaTouchLessSeekAction));
+      GetLocale().QueryString(IDS_AX_MEDIA_TOUCHLESS_SEEK_ACTION));
   aria_label.Append(" ");
-  aria_label.Append(GetLocale().QueryString(
-      WebLocalizedString::kAXMediaTouchLessVolumeAction));
+  aria_label.Append(
+      GetLocale().QueryString(IDS_AX_MEDIA_TOUCHLESS_VOLUME_ACTION));
   setAttribute(html_names::kAriaLabelAttr, aria_label.ToAtomicString());
 }
 
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.cc b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.cc
index 5c6db6b..4387b28 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.cc
@@ -51,11 +51,11 @@
 
   StringBuilder aria_label;
   aria_label.Append(GetLocale().QueryString(
-      WebLocalizedString::kAXMediaCurrentTimeDisplay,
+      IDS_AX_MEDIA_CURRENT_TIME_DISPLAY,
       MediaControlsSharedHelpers::FormatTime(current_time_)));
   aria_label.Append(" ");
   aria_label.Append(GetLocale().QueryString(
-      WebLocalizedString::kAXMediaTimeRemainingDisplay,
+      IDS_AX_MEDIA_TIME_REMAINING_DISPLAY,
       MediaControlsSharedHelpers::FormatTime(duration_)));
   setAttribute(html_names::kAriaLabelAttr, aria_label.ToAtomicString());
 }
diff --git a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
index b5490ca..badb26c 100644
--- a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
+++ b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
@@ -9,7 +9,7 @@
 
 #include "base/bind_helpers.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/common/manifest/web_display_mode.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -423,7 +423,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       GetSupplementable()->GetFrame()->GetTaskRunner(
           TaskType::kMediaElementEvent);
-  GetSupplementable()->GetFrame()->GetInterfaceProvider().GetInterface(
+  GetSupplementable()->GetBrowserInterfaceBroker().GetInterface(
       picture_in_picture_service_.BindNewPipeAndPassReceiver(task_runner));
   return true;
 }
diff --git a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_test.cc b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_test.cc
index 169a910..0d44eff 100644
--- a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_test.cc
+++ b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_test.cc
@@ -9,6 +9,7 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/picture_in_picture/picture_in_picture.mojom-blink.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
@@ -116,16 +117,9 @@
 
   explicit PictureInPictureControllerFrameClient(
       std::unique_ptr<WebMediaPlayer> player)
-      : test::MediaStubLocalFrameClient(std::move(player)),
-        interface_provider_(new service_manager::InterfaceProvider()) {}
-
-  service_manager::InterfaceProvider* GetInterfaceProvider() override {
-    return interface_provider_.get();
-  }
+      : test::MediaStubLocalFrameClient(std::move(player)) {}
 
  private:
-  std::unique_ptr<service_manager::InterfaceProvider> interface_provider_;
-
   DISALLOW_COPY_AND_ASSIGN(PictureInPictureControllerFrameClient);
 };
 
@@ -158,9 +152,7 @@
         nullptr, PictureInPictureControllerFrameClient::Create(
                      std::make_unique<PictureInPictureControllerPlayer>()));
 
-    service_manager::InterfaceProvider::TestApi test_api(
-        GetFrame().Client()->GetInterfaceProvider());
-    test_api.SetBinderForName(
+    GetDocument().GetBrowserInterfaceBroker().SetBinderForTesting(
         mojom::blink::PictureInPictureService::Name_,
         WTF::BindRepeating(&MockPictureInPictureService::Bind,
                            WTF::Unretained(&mock_service_)));
@@ -184,6 +176,11 @@
     test::RunPendingTasks();
   }
 
+  void TearDown() override {
+    GetDocument().GetBrowserInterfaceBroker().SetBinderForTesting(
+        mojom::blink::PictureInPictureService::Name_, {});
+  }
+
   HTMLVideoElement* Video() const { return video_.Get(); }
   MockPictureInPictureService& Service() { return mock_service_; }
 
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 46f0a578..da7d8f1b 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
@@ -238,6 +238,12 @@
     const v8_inspector::V8StackTraceId& stack_id) {
   DCHECK(!IsContextPaused());
 
+  if (installed_scripts_manager_) {
+    // This service worker is installed. Load and run the installed script.
+    LoadAndRunInstalledClassicScript(script_url, stack_id);
+    return;
+  }
+
   // Step 9. "Switching on job's worker type, run these substeps with the
   // following options:"
   // "classic: Fetch a classic worker script given job's serialized script url,
@@ -284,6 +290,11 @@
     WorkerResourceTimingNotifier& outside_resource_timing_notifier,
     network::mojom::CredentialsMode credentials_mode) {
   DCHECK(IsContextThread());
+
+  // Start fetching scripts regardless of whether this service worker is
+  // installed. For the installed case, the scripts will be read from the
+  // service worker script storage during module script fetch (see
+  // InstalledServiceWorkerModuleScriptFetcher).
   FetchModuleScript(module_url_record, outside_settings_object,
                     outside_resource_timing_notifier,
                     mojom::RequestContextType::SERVICE_WORKER, credentials_mode,
@@ -292,67 +303,6 @@
                         Modulator::From(ScriptController()->GetScriptState())));
 }
 
-void ServiceWorkerGlobalScope::RunInstalledClassicScript(
-    const KURL& script_url,
-    const v8_inspector::V8StackTraceId& stack_id) {
-  DCHECK(IsContextThread());
-
-  DCHECK(installed_scripts_manager_);
-  DCHECK(installed_scripts_manager_->IsScriptInstalled(script_url));
-
-  // GetScriptData blocks until the script is received from the browser.
-  std::unique_ptr<InstalledScriptsManager::ScriptData> script_data =
-      installed_scripts_manager_->GetScriptData(script_url);
-  if (!script_data) {
-    ReportingProxy().DidFailToFetchClassicScript();
-    // This will eventually initiate worker thread termination. See
-    // ServiceWorkerGlobalScopeProxy::DidCloseWorkerGlobalScope() for details.
-    close();
-    return;
-  }
-  ReportingProxy().DidLoadClassicScript();
-
-  auto referrer_policy = network::mojom::ReferrerPolicy::kDefault;
-  if (!script_data->GetReferrerPolicy().IsNull()) {
-    SecurityPolicy::ReferrerPolicyFromHeaderValue(
-        script_data->GetReferrerPolicy(),
-        kDoNotSupportReferrerPolicyLegacyKeywords, &referrer_policy);
-  }
-
-  // Construct a ContentSecurityPolicy object to convert
-  // ContentSecurityPolicyResponseHeaders to CSPHeaderAndType.
-  // TODO(nhiroki): Find an efficient way to do this.
-  auto* content_security_policy = MakeGarbageCollected<ContentSecurityPolicy>();
-  content_security_policy->DidReceiveHeaders(
-      script_data->GetContentSecurityPolicyResponseHeaders());
-
-  RunClassicScript(
-      script_url, referrer_policy, script_data->GetResponseAddressSpace(),
-      content_security_policy->Headers(),
-      script_data->CreateOriginTrialTokens().get(),
-      script_data->TakeSourceText(), script_data->TakeMetaData(), stack_id);
-}
-
-void ServiceWorkerGlobalScope::RunInstalledModuleScript(
-    const KURL& module_url_record,
-    const FetchClientSettingsObjectSnapshot& outside_settings_object,
-    network::mojom::CredentialsMode credentials_mode) {
-  DCHECK(IsContextThread());
-  // Currently we don't plumb performance timing for toplevel service worker
-  // script fetch. https://crbug.com/954005
-  auto* outside_resource_timing_notifier =
-      MakeGarbageCollected<NullWorkerResourceTimingNotifier>();
-
-  // The installed scripts will be read from the service worker script storage
-  // during module script fetch.
-  FetchModuleScript(module_url_record, outside_settings_object,
-                    *outside_resource_timing_notifier,
-                    mojom::RequestContextType::SERVICE_WORKER, credentials_mode,
-                    ModuleScriptCustomFetchType::kInstalledServiceWorker,
-                    MakeGarbageCollected<ServiceWorkerModuleTreeClient>(
-                        Modulator::From(ScriptController()->GetScriptState())));
-}
-
 void ServiceWorkerGlobalScope::Dispose() {
   DCHECK(IsContextThread());
   controller_receivers_.Clear();
@@ -544,6 +494,47 @@
   DCHECK_EQ(appcache_id, mojom::blink::kAppCacheNoCacheId);
 }
 
+void ServiceWorkerGlobalScope::LoadAndRunInstalledClassicScript(
+    const KURL& script_url,
+    const v8_inspector::V8StackTraceId& stack_id) {
+  DCHECK(IsContextThread());
+
+  DCHECK(installed_scripts_manager_);
+  DCHECK(installed_scripts_manager_->IsScriptInstalled(script_url));
+
+  // GetScriptData blocks until the script is received from the browser.
+  std::unique_ptr<InstalledScriptsManager::ScriptData> script_data =
+      installed_scripts_manager_->GetScriptData(script_url);
+  if (!script_data) {
+    ReportingProxy().DidFailToFetchClassicScript();
+    // This will eventually initiate worker thread termination. See
+    // ServiceWorkerGlobalScopeProxy::DidCloseWorkerGlobalScope() for details.
+    close();
+    return;
+  }
+  ReportingProxy().DidLoadClassicScript();
+
+  auto referrer_policy = network::mojom::ReferrerPolicy::kDefault;
+  if (!script_data->GetReferrerPolicy().IsNull()) {
+    SecurityPolicy::ReferrerPolicyFromHeaderValue(
+        script_data->GetReferrerPolicy(),
+        kDoNotSupportReferrerPolicyLegacyKeywords, &referrer_policy);
+  }
+
+  // Construct a ContentSecurityPolicy object to convert
+  // ContentSecurityPolicyResponseHeaders to CSPHeaderAndType.
+  // TODO(nhiroki): Find an efficient way to do this.
+  auto* content_security_policy = MakeGarbageCollected<ContentSecurityPolicy>();
+  content_security_policy->DidReceiveHeaders(
+      script_data->GetContentSecurityPolicyResponseHeaders());
+
+  RunClassicScript(
+      script_url, referrer_policy, script_data->GetResponseAddressSpace(),
+      content_security_policy->Headers(),
+      script_data->CreateOriginTrialTokens().get(),
+      script_data->TakeSourceText(), script_data->TakeMetaData(), stack_id);
+}
+
 // https://w3c.github.io/ServiceWorker/#run-service-worker-algorithm
 void ServiceWorkerGlobalScope::RunClassicScript(
     const KURL& response_url,
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 166c5ad8..884002a 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
@@ -108,15 +108,13 @@
                   const Vector<CSPHeaderAndType>& response_csp_headers,
                   const Vector<String>* response_origin_trial_tokens,
                   int64_t appcache_id) override;
-  // Fetches and runs the top-level classic worker script for the 'new' or
-  // 'update' service worker cases.
+  // Fetches and runs the top-level classic worker script.
   void FetchAndRunClassicScript(
       const KURL& script_url,
       const FetchClientSettingsObjectSnapshot& outside_settings_object,
       WorkerResourceTimingNotifier& outside_resource_timing_notifier,
       const v8_inspector::V8StackTraceId& stack_id) override;
-  // Fetches and runs the top-level module worker script for the 'new' or
-  // 'update' service worker cases.
+  // Fetches and runs the top-level module worker script.
   void FetchAndRunModuleScript(
       const KURL& module_url_record,
       const FetchClientSettingsObjectSnapshot& outside_settings_object,
@@ -125,18 +123,6 @@
   void Dispose() override;
   InstalledScriptsManager* GetInstalledScriptsManager() override;
 
-  // Runs the installed top-level classic worker script for the 'installed'
-  // service worker case.
-  void RunInstalledClassicScript(const KURL& script_url,
-                                 const v8_inspector::V8StackTraceId& stack_id);
-
-  // Runs the installed top-level module worker script for the 'installed'
-  // service worker case.
-  void RunInstalledModuleScript(
-      const KURL& module_url_record,
-      const FetchClientSettingsObjectSnapshot& outside_settings_object,
-      network::mojom::CredentialsMode);
-
   // Counts an evaluated script and its size. Called for the main worker script.
   void CountWorkerScript(size_t script_size, size_t cached_metadata_size);
 
@@ -332,6 +318,11 @@
   void DidFetchClassicScript(WorkerClassicScriptLoader* classic_script_loader,
                              const v8_inspector::V8StackTraceId& stack_id);
 
+  // Loads and runs the installed top-level classic worker script.
+  void LoadAndRunInstalledClassicScript(
+      const KURL& script_url,
+      const v8_inspector::V8StackTraceId& stack_id);
+
   // https://w3c.github.io/ServiceWorker/#run-service-worker-algorithm
   void RunClassicScript(const KURL& response_url,
                         network::mojom::ReferrerPolicy response_referrer_policy,
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.cc b/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.cc
index c265bf8..c06bd7c 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.cc
@@ -9,7 +9,10 @@
 
 #include "base/barrier_closure.h"
 #include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "third_party/blink/public/web/web_embedded_worker.h"
 #include "third_party/blink/renderer/core/html/parser/text_resource_decoder.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_thread.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
@@ -245,18 +248,33 @@
 }  // namespace
 
 ServiceWorkerInstalledScriptsManager::ServiceWorkerInstalledScriptsManager(
-    const Vector<KURL>& installed_urls,
-    mojo::PendingReceiver<mojom::blink::ServiceWorkerInstalledScriptsManager>
-        manager_receiver,
-    mojo::PendingRemote<mojom::blink::ServiceWorkerInstalledScriptsManagerHost>
-        manager_host,
+    std::unique_ptr<WebServiceWorkerInstalledScriptsManagerParams>
+        installed_scripts_manager_params,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
-    : script_container_(base::MakeRefCounted<ThreadSafeScriptContainer>()),
-      manager_host_(std::move(manager_host)) {
-  // We're on the main thread now, but |installed_urls_| will be accessed on the
-  // worker thread later, so make a deep copy of |url| as key.
-  for (const KURL& url : installed_urls)
-    installed_urls_.insert(url.Copy());
+    : script_container_(base::MakeRefCounted<ThreadSafeScriptContainer>()) {
+  DCHECK(installed_scripts_manager_params);
+
+  DCHECK(installed_scripts_manager_params->manager_receiver);
+  auto manager_receiver =
+      mojo::PendingReceiver<mojom::blink::ServiceWorkerInstalledScriptsManager>(
+          std::move(installed_scripts_manager_params->manager_receiver));
+
+  DCHECK(installed_scripts_manager_params->manager_host_remote);
+  manager_host_ = mojo::SharedRemote<
+      mojom::blink::ServiceWorkerInstalledScriptsManagerHost>(
+      mojo::PendingRemote<
+          mojom::blink::ServiceWorkerInstalledScriptsManagerHost>(
+          std::move(installed_scripts_manager_params->manager_host_remote),
+          mojom::blink::ServiceWorkerInstalledScriptsManagerHost::Version_));
+
+  // Don't touch |installed_urls_| after this point. We're on the initiator
+  // thread now, but |installed_urls_| will be accessed on the
+  // worker thread later, so they should keep isolated from the current thraed.
+  for (const WebURL& url :
+       installed_scripts_manager_params->installed_scripts_urls) {
+    installed_urls_.insert(url);
+  }
+
   PostCrossThreadTask(
       *io_task_runner, FROM_HERE,
       CrossThreadBindOnce(&Internal::Create, script_container_,
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.h b/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.h
index 91f9aea..3c7f880 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_INSTALLED_SCRIPTS_MANAGER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_INSTALLED_SCRIPTS_MANAGER_H_
 
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/shared_remote.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom-blink.h"
 #include "third_party/blink/renderer/core/workers/installed_scripts_manager.h"
@@ -19,17 +17,16 @@
 
 namespace blink {
 
+struct WebServiceWorkerInstalledScriptsManagerParams;
+
 // ServiceWorkerInstalledScriptsManager provides the main script and imported
 // scripts of an installed service worker. The scripts are streamed from the
 // browser process in parallel with worker thread initialization.
-class MODULES_EXPORT ServiceWorkerInstalledScriptsManager
+class MODULES_EXPORT ServiceWorkerInstalledScriptsManager final
     : public InstalledScriptsManager {
  public:
   ServiceWorkerInstalledScriptsManager(
-      const Vector<KURL>& installed_urls,
-      mojo::PendingReceiver<mojom::blink::ServiceWorkerInstalledScriptsManager>,
-      mojo::PendingRemote<
-          mojom::blink::ServiceWorkerInstalledScriptsManagerHost>,
+      std::unique_ptr<WebServiceWorkerInstalledScriptsManagerParams>,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
   virtual ~ServiceWorkerInstalledScriptsManager() = default;
 
@@ -43,7 +40,10 @@
   std::unique_ptr<ThreadSafeScriptContainer::RawScriptData> GetRawScriptData(
       const KURL& script_url);
 
+  // Constructed on the initiator thread, and accessed only on the worker
+  // thread.
   HashSet<KURL> installed_urls_;
+
   scoped_refptr<ThreadSafeScriptContainer> script_container_;
   mojo::SharedRemote<mojom::blink::ServiceWorkerInstalledScriptsManagerHost>
       manager_host_;
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager_test.cc b/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager_test.cc
index 8f78882..2e6ef8e4 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager_test.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager_test.cc
@@ -11,6 +11,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/web/web_embedded_worker.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
@@ -140,11 +141,14 @@
   void CreateInstalledScriptsManager(
       mojom::blink::ServiceWorkerInstalledScriptsInfoPtr
           installed_scripts_info) {
+    auto installed_scripts_manager_params =
+        std::make_unique<WebServiceWorkerInstalledScriptsManagerParams>(
+            std::move(installed_scripts_info->installed_urls),
+            installed_scripts_info->manager_receiver.PassPipe(),
+            installed_scripts_info->manager_host_remote.PassPipe());
     installed_scripts_manager_ =
         std::make_unique<ServiceWorkerInstalledScriptsManager>(
-            std::move(installed_scripts_info->installed_urls),
-            std::move(installed_scripts_info->manager_receiver),
-            std::move(installed_scripts_info->manager_host_remote),
+            std::move(installed_scripts_manager_params),
             io_thread_->GetTaskRunner());
   }
 
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_thread.cc b/third_party/blink/renderer/modules/service_worker/service_worker_thread.cc
index 5faa587..c8f8a946 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_thread.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_thread.cc
@@ -32,16 +32,12 @@
 
 #include <memory>
 
-#include "third_party/blink/renderer/core/inspector/thread_debugger.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
 #include "third_party/blink/renderer/core/workers/worker_backing_thread.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
-#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
-#include "v8/include/v8-inspector.h"
 
 namespace blink {
 
@@ -73,54 +69,6 @@
   WorkerThread::TerminateForTesting();
 }
 
-void ServiceWorkerThread::RunInstalledClassicScript(
-    const KURL& script_url,
-    const v8_inspector::V8StackTraceId& stack_id) {
-  // Use TaskType::kDOMManipulation for consistency with
-  // WorkerThread::EvaluateClassicScript().
-  PostCrossThreadTask(
-      *GetTaskRunner(TaskType::kDOMManipulation), FROM_HERE,
-      CrossThreadBindOnce(
-          &ServiceWorkerThread::RunInstalledClassicScriptOnWorkerThread,
-          CrossThreadUnretained(this), script_url, stack_id));
-}
-
-void ServiceWorkerThread::RunInstalledModuleScript(
-    const KURL& module_url_record,
-    std::unique_ptr<CrossThreadFetchClientSettingsObjectData>
-        outside_settings_object_data,
-    network::mojom::CredentialsMode credentials_mode) {
-  PostCrossThreadTask(
-      *GetTaskRunner(TaskType::kDOMManipulation), FROM_HERE,
-      CrossThreadBindOnce(
-          &ServiceWorkerThread::RunInstalledModuleScriptOnWorkerThread,
-          CrossThreadUnretained(this), module_url_record,
-          WTF::Passed(std::move(outside_settings_object_data)),
-          credentials_mode));
-}
-
-void ServiceWorkerThread::RunInstalledClassicScriptOnWorkerThread(
-    const KURL& script_url,
-    const v8_inspector::V8StackTraceId& stack_id) {
-  DCHECK(IsCurrentThread());
-  To<ServiceWorkerGlobalScope>(GlobalScope())
-      ->RunInstalledClassicScript(script_url, stack_id);
-}
-
-void ServiceWorkerThread::RunInstalledModuleScriptOnWorkerThread(
-    const KURL& module_url_record,
-    std::unique_ptr<CrossThreadFetchClientSettingsObjectData>
-        outside_settings_object,
-    network::mojom::CredentialsMode credentials_mode) {
-  DCHECK(IsCurrentThread());
-  To<ServiceWorkerGlobalScope>(GlobalScope())
-      ->RunInstalledModuleScript(
-          module_url_record,
-          *MakeGarbageCollected<FetchClientSettingsObjectSnapshot>(
-              std::move(outside_settings_object)),
-          credentials_mode);
-}
-
 WorkerOrWorkletGlobalScope* ServiceWorkerThread::CreateWorkerGlobalScope(
     std::unique_ptr<GlobalScopeCreationParams> creation_params) {
   return ServiceWorkerGlobalScope::Create(
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_thread.h b/third_party/blink/renderer/modules/service_worker/service_worker_thread.h
index bb963eb3..f6af8f55 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_thread.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_thread.h
@@ -62,14 +62,6 @@
   void ClearWorkerBackingThread() override;
   void TerminateForTesting() override;
 
-  void RunInstalledClassicScript(const KURL& script_url,
-                                 const v8_inspector::V8StackTraceId& stack_id);
-  void RunInstalledModuleScript(
-      const KURL& module_url_record,
-      std::unique_ptr<CrossThreadFetchClientSettingsObjectData>
-          outside_settings_object_data,
-      network::mojom::CredentialsMode);
-
  private:
   WorkerOrWorkletGlobalScope* CreateWorkerGlobalScope(
       std::unique_ptr<GlobalScopeCreationParams>) override;
@@ -78,15 +70,6 @@
     return WebThreadType::kServiceWorkerThread;
   }
 
-  void RunInstalledClassicScriptOnWorkerThread(
-      const KURL& script_url,
-      const v8_inspector::V8StackTraceId& stack_id);
-  void RunInstalledModuleScriptOnWorkerThread(
-      const KURL& module_url_record,
-      std::unique_ptr<CrossThreadFetchClientSettingsObjectData>
-          outside_settings_object,
-      network::mojom::CredentialsMode);
-
   std::unique_ptr<ServiceWorkerGlobalScopeProxy> global_scope_proxy_;
   std::unique_ptr<WorkerBackingThread> worker_backing_thread_;
 
diff --git a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
index d071e66a..0ac2717 100644
--- a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
+++ b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
@@ -192,10 +192,7 @@
  protected:
   void SetUp() override {
     mock_client_ = std::make_unique<MockServiceWorkerContextClient>();
-    worker_ = std::make_unique<WebEmbeddedWorkerImpl>(
-        mock_client_.get(), /*cache_storage_remote=*/mojo::NullRemote(),
-        /*interface_provider_info=*/nullptr,
-        /*browser_interface_broker=*/mojo::NullRemote());
+    worker_ = std::make_unique<WebEmbeddedWorkerImpl>(mock_client_.get());
 
     script_url_ = url_test_helpers::ToKURL("https://www.example.com/sw.js");
     WebURLResponse response(script_url_);
@@ -241,6 +238,9 @@
       CreateStartData(),
       /*installed_scripts_manager_params=*/nullptr,
       /*content_settings_proxy=*/mojo::ScopedMessagePipeHandle(),
+      /*cache_storage_remote=*/mojo::ScopedMessagePipeHandle(),
+      /*interface_provider_info=*/mojo::ScopedMessagePipeHandle(),
+      /*browser_interface_broker=*/mojo::ScopedMessagePipeHandle(),
       Thread::Current()->GetTaskRunner());
   testing::Mock::VerifyAndClearExpectations(mock_client_.get());
 
@@ -257,6 +257,9 @@
       std::move(start_data),
       /*installed_scripts_manager_params=*/nullptr,
       /*content_settings_proxy=*/mojo::ScopedMessagePipeHandle(),
+      /*cache_storage_remote=*/mojo::ScopedMessagePipeHandle(),
+      /*interface_provider_info=*/mojo::ScopedMessagePipeHandle(),
+      /*browser_interface_broker=*/mojo::ScopedMessagePipeHandle(),
       Thread::Current()->GetTaskRunner());
   testing::Mock::VerifyAndClearExpectations(mock_client_.get());
 
@@ -273,6 +276,9 @@
       CreateStartData(),
       /*installed_scripts_manager_params=*/nullptr,
       /*content_settings_proxy=*/mojo::ScopedMessagePipeHandle(),
+      /*cache_storage_remote=*/mojo::ScopedMessagePipeHandle(),
+      /*interface_provider_info=*/mojo::ScopedMessagePipeHandle(),
+      /*browser_interface_broker=*/mojo::ScopedMessagePipeHandle(),
       Thread::Current()->GetTaskRunner());
   testing::Mock::VerifyAndClearExpectations(mock_client_.get());
 
@@ -297,6 +303,9 @@
       std::move(start_data),
       /*installed_scripts_manager_params=*/nullptr,
       /*content_settings_proxy=*/mojo::ScopedMessagePipeHandle(),
+      /*cache_storage_remote=*/mojo::ScopedMessagePipeHandle(),
+      /*interface_provider_info=*/mojo::ScopedMessagePipeHandle(),
+      /*browser_interface_broker=*/mojo::ScopedMessagePipeHandle(),
       Thread::Current()->GetTaskRunner());
   testing::Mock::VerifyAndClearExpectations(mock_client_.get());
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_pipeline_descriptor.idl b/third_party/blink/renderer/modules/webgpu/gpu_render_pipeline_descriptor.idl
index 4fe019c..f78a424 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_render_pipeline_descriptor.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_render_pipeline_descriptor.idl
@@ -12,7 +12,7 @@
     GPURasterizationStateDescriptor rasterizationState;
     required sequence<GPUColorStateDescriptor> colorStates;
     GPUDepthStencilStateDescriptor depthStencilState;
-    GPUVertexInputDescriptor vertexInput;
+    GPUVertexInputDescriptor vertexInput = {};
 
     unsigned long sampleCount = 1;
 
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc b/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc
index 0a06b0c..43d0b66 100644
--- a/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc
@@ -21,6 +21,7 @@
     case Feature::kWebGL:
     case Feature::kWebVR:
     case Feature::kWebXR:
+    case Feature::kSharedWorker:
       return false;
     case Feature::kMainResourceHasCacheControlNoStore:
     case Feature::kMainResourceHasCacheControlNoCache:
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG
index 5b5fbbb7..654fcbd 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG
@@ -37,9 +37,6 @@
 crbug.com/591099 external/wpt/css/css-fonts/font-features-across-space-1.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-fonts/font-features-across-space-3.html [ Failure ]
 
-### external/wpt/css/css-grid/grid-items/
-crbug.com/591099 external/wpt/css/css-grid/grid-items/grid-item-percentage-sizes-003.html [ Failure ]
-
 ### external/wpt/css/css-layout-api/
 crbug.com/591099 external/wpt/css/css-layout-api/ [ Skip ]
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index ed95ce6..5298987 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6200,3 +6200,7 @@
 
 # Sheriff 2019-09-20
 crbug.com/1005128 crypto/subtle/abandon-crypto-operation2.html [ Pass Crash ]
+
+# Sheriff 2019-09-26
+crbug.com/1008257 external/wpt/service-workers/service-worker/claim-shared-worker-fetch.https.html [ Pass Crash ]
+crbug.com/1008257 external/wpt/service-workers/service-worker/worker-interception-redirect.https.html [ Pass Crash ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index 5159b1a6..0ae9663e 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -213207,6 +213207,12 @@
      {}
     ]
    ],
+   "css/css-text/white-space/pre-with-whitespace-crash.html": [
+    [
+     "css/css-text/white-space/pre-with-whitespace-crash.html",
+     {}
+    ]
+   ],
    "css/css-text/white-space/seg-break-transformation-000.html": [
     [
      "css/css-text/white-space/seg-break-transformation-000.html",
@@ -389642,7 +389648,7 @@
    "visual"
   ],
   "css/css-text/text-align/text-align-last-empty-inline.html": [
-   "07dcb3ba896c1f393896a397775cc3ff83fb5ef3",
+   "9bf36815c9618573a3c53779815ad914c9020102",
    "testharness"
   ],
   "css/css-text/text-align/text-align-start-001.html": [
@@ -391085,6 +391091,10 @@
    "c7e1855f9794f42f965e64fc390e1b50b83059fb",
    "testharness"
   ],
+  "css/css-text/white-space/pre-with-whitespace-crash.html": [
+   "89e5c3a71feb52183061da6b1e5906cda185ea04",
+   "testharness"
+  ],
   "css/css-text/white-space/pre-wrap-001.html": [
    "87ffad96310532a6501e1b75c30f986433cc634e",
    "reftest"
@@ -478830,7 +478840,7 @@
    "support"
   ],
   "resources/chromium/generic_sensor_mocks.js": [
-   "d4ba89e199113b2aea9f393f0d38461ea1d30631",
+   "4f1370236876f4d6a919e3fad1175eaef6ae2850",
    "support"
   ],
   "resources/chromium/generic_sensor_mocks.js.headers": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-align/text-align-last-empty-inline.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-align/text-align-last-empty-inline.html
index 07dcb3b..9bf3681 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/text-align/text-align-last-empty-inline.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-align/text-align-last-empty-inline.html
@@ -13,10 +13,13 @@
     padding: 1px;
     background: black;
   }
+  #reference {
+    text-align: center;
+  }
 </style>
 <body>
-  <div id="justify"><span></span></div>
-  <div id="reference"><span></span></div>
+  <div id="justify"><span>a</span></div>
+  <div id="reference"><span>a</span></div>
   <p>
     The two black line segments above should align.
   </p>
@@ -29,5 +32,5 @@
   const ref_rect = ref_element.firstElementChild.getBoundingClientRect();
   assert_equals(justify_rect.left, ref_rect.left);
   assert_equals(justify_rect.right, ref_rect.right);
-}, 'Left and right edges of empty inlines should align.');
+}, 'content that cannot be justified should be centered when text-align-last is justify');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-with-whitespace-crash.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-with-whitespace-crash.html
new file mode 100644
index 0000000..89e5c3a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-with-whitespace-crash.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+<head>
+  <title>pre(white-space's one of values) crash if there is no available width</title>
+  <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=999863">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script>test(()=>{})</script>
+</head>
+<body>
+  abc<b style="margin-left: 100%; white-space: pre;">def</b>
+  <div>
+    <li>
+      <a href="dummy">success if does not crash</a>
+    </li>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-box/fill-box-mutation.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-box/fill-box-mutation-001.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-transforms/transform-box/fill-box-mutation.html
rename to third_party/blink/web_tests/external/wpt/css/css-transforms/transform-box/fill-box-mutation-001.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-box/fill-box-mutation-002.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-box/fill-box-mutation-002.html
new file mode 100644
index 0000000..676d5cd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform-box/fill-box-mutation-002.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>transform-box: fill-box, image mutated</title>
+<link rel="match" href="reference/greensquare200x200.html">
+<link rel="help" href="https://drafts.csswg.org/css-transforms/#transform-box">
+<style>
+#target {
+  transform-box: fill-box;
+  transform: translate(-50%, 0);
+}
+</style>
+<p>There should be a green 200x200 rectangle below, and no red.</p>
+<svg width="400" height="200">
+  <rect width="200" height="200" fill="red"/>
+  <image id="target" x="100" width="100" height="200"
+         href="/css/css-transforms/support/1x1-green.png"/>
+</svg>
+<script>
+requestAnimationFrame(function() {
+  requestAnimationFrame(function() {
+    document.querySelector('#target').setAttribute('width', 200);
+    document.documentElement.classList.remove('reftest-wait');
+  });
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-angle-serialize-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-angle-serialize-expected.txt
new file mode 100644
index 0000000..08cc9c44
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-angle-serialize-expected.txt
@@ -0,0 +1,25 @@
+This is a testharness.js-based test.
+PASS e.style['transform'] = "rotate(min(1deg))" should set the property value
+PASS e.style['transform'] = "rotate(min(1rad))" should set the property value
+PASS e.style['transform'] = "rotate(min(1turn))" should set the property value
+PASS e.style['transform'] = "rotate(min(1grad))" should set the property value
+PASS e.style['transform'] = "rotate(max(1deg))" should set the property value
+PASS e.style['transform'] = "rotate(max(1rad))" should set the property value
+PASS e.style['transform'] = "rotate(max(1turn))" should set the property value
+PASS e.style['transform'] = "rotate(max(1grad))" should set the property value
+PASS e.style['transform'] = "rotate(min(1deg, 2deg, 3deg))" should set the property value
+PASS e.style['transform'] = "rotate(min(3deg, 2deg, 1deg))" should set the property value
+PASS e.style['transform'] = "rotate(min(90deg, 1.57rad, 0.25turn))" should set the property value
+PASS e.style['transform'] = "rotate(min(0.25turn, 1.57rad, 90deg))" should set the property value
+PASS e.style['transform'] = "rotate(max(1deg, 2deg, 3deg))" should set the property value
+PASS e.style['transform'] = "rotate(max(3deg, 2deg, 1deg))" should set the property value
+PASS e.style['transform'] = "rotate(max(90deg, 1.57rad, 0.25turn))" should set the property value
+PASS e.style['transform'] = "rotate(max(0.25turn, 1.57rad, 90deg))" should set the property value
+PASS e.style['transform'] = "rotate(calc(min(1deg) + min(2deg)))" should set the property value
+PASS e.style['transform'] = "rotate(calc(max(1deg) + max(2deg)))" should set the property value
+PASS e.style['transform'] = "rotate(calc(1rad + min(1deg)))" should set the property value
+FAIL e.style['transform'] = "rotate(calc(min(1deg) + 1rad))" should set the property value assert_equals: serialization should be canonical expected "rotate(calc(1rad + min(1deg)))" but got "rotate(calc(min(1deg) + 1rad))"
+PASS e.style['transform'] = "rotate(calc(1rad + max(1deg)))" should set the property value
+FAIL e.style['transform'] = "rotate(calc(max(1deg) + 1rad))" should set the property value assert_equals: serialization should be canonical expected "rotate(calc(1rad + max(1deg)))" but got "rotate(calc(max(1deg) + 1rad))"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-angle-serialize.html b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-angle-serialize.html
index 3f42458..07b7d3c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-angle-serialize.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-angle-serialize.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
 <link rel="help" href="https://drafts.csswg.org/css-values-4/#angles">
-<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-type-checking">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-serialize">
 <link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
@@ -20,6 +20,20 @@
 test_valid_angle('max(1turn)', 'max(1turn)');
 test_valid_angle('max(1grad)', 'max(1grad)');
 
-// TODO(crbug.com/978682): Complete this test suite
+test_valid_angle('min(1deg, 2deg, 3deg)', 'min(1deg, 2deg, 3deg)');
+test_valid_angle('min(3deg, 2deg, 1deg)', 'min(3deg, 2deg, 1deg)');
+test_valid_angle('min(90deg, 1.57rad, 0.25turn)', 'min(90deg, 1.57rad, 0.25turn)');
+test_valid_angle('min(0.25turn, 1.57rad, 90deg)', 'min(0.25turn, 1.57rad, 90deg)');
+test_valid_angle('max(1deg, 2deg, 3deg)', 'max(1deg, 2deg, 3deg)');
+test_valid_angle('max(3deg, 2deg, 1deg)', 'max(3deg, 2deg, 1deg)');
+test_valid_angle('max(90deg, 1.57rad, 0.25turn)', 'max(90deg, 1.57rad, 0.25turn)');
+test_valid_angle('max(0.25turn, 1.57rad, 90deg)', 'max(0.25turn, 1.57rad, 90deg)');
+
+test_valid_angle('calc(min(1deg) + min(2deg))', 'calc(min(1deg) + min(2deg))');
+test_valid_angle('calc(max(1deg) + max(2deg))', 'calc(max(1deg) + max(2deg))');
+test_valid_angle('calc(1rad + min(1deg))', 'calc(1rad + min(1deg))');
+test_valid_angle('calc(min(1deg) + 1rad)', 'calc(1rad + min(1deg))');
+test_valid_angle('calc(1rad + max(1deg))', 'calc(1rad + max(1deg))');
+test_valid_angle('calc(max(1deg) + 1rad)', 'calc(1rad + max(1deg))');
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-time-serialize-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-time-serialize-expected.txt
new file mode 100644
index 0000000..b6f5f02c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-time-serialize-expected.txt
@@ -0,0 +1,23 @@
+This is a testharness.js-based test.
+PASS e.style['transition-delay'] = "min(1ms)" should set the property value
+PASS e.style['transition-delay'] = "min(1s)" should set the property value
+PASS e.style['transition-delay'] = "max(1ms)" should set the property value
+PASS e.style['transition-delay'] = "max(1s)" should set the property value
+PASS e.style['transition-delay'] = "min(1ms, 2ms, 3ms)" should set the property value
+PASS e.style['transition-delay'] = "min(3ms, 2ms, 1ms)" should set the property value
+PASS e.style['transition-delay'] = "max(1ms, 2ms, 3ms)" should set the property value
+PASS e.style['transition-delay'] = "max(3ms, 2ms, 1ms)" should set the property value
+PASS e.style['transition-delay'] = "min(1000ms, 1s)" should set the property value
+PASS e.style['transition-delay'] = "min(1s, 1000ms)" should set the property value
+PASS e.style['transition-delay'] = "max(1000ms, 1s)" should set the property value
+PASS e.style['transition-delay'] = "max(1s, 1000ms)" should set the property value
+PASS e.style['transition-delay'] = "calc(min(1s) + min(2s))" should set the property value
+PASS e.style['transition-delay'] = "calc(min(2s) + min(1s))" should set the property value
+PASS e.style['transition-delay'] = "calc(max(1s) + max(2s))" should set the property value
+PASS e.style['transition-delay'] = "calc(max(2s) + max(1s))" should set the property value
+PASS e.style['transition-delay'] = "calc(1s + min(2s))" should set the property value
+FAIL e.style['transition-delay'] = "calc(min(2s) + 1s)" should set the property value assert_equals: serialization should be canonical expected "calc(1s + min(2s))" but got "calc(min(2s) + 1s)"
+PASS e.style['transition-delay'] = "calc(1s + max(2s))" should set the property value
+FAIL e.style['transition-delay'] = "calc(max(2s) + 1s)" should set the property value assert_equals: serialization should be canonical expected "calc(1s + max(2s))" but got "calc(max(2s) + 1s)"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-time-serialize.html b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-time-serialize.html
new file mode 100644
index 0000000..d7dd4edb5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-time-serialize.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#time">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-serialize">
+<link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/parsing-testcommon.js"></script>
+<script>
+function test_valid_time(value, expected) {
+  test_valid_value('transition-delay', value, expected);
+}
+
+test_valid_time('min(1ms)', 'min(1ms)');
+test_valid_time('min(1s)', 'min(1s)');
+test_valid_time('max(1ms)', 'max(1ms)');
+test_valid_time('max(1s)', 'max(1s)');
+
+test_valid_time('min(1ms, 2ms, 3ms)', 'min(1ms, 2ms, 3ms)');
+test_valid_time('min(3ms, 2ms, 1ms)', 'min(3ms, 2ms, 1ms)');
+test_valid_time('max(1ms, 2ms, 3ms)', 'max(1ms, 2ms, 3ms)');
+test_valid_time('max(3ms, 2ms, 1ms)', 'max(3ms, 2ms, 1ms)');
+test_valid_time('min(1000ms, 1s)', 'min(1000ms, 1s)');
+test_valid_time('min(1s, 1000ms)', 'min(1s, 1000ms)');
+test_valid_time('max(1000ms, 1s)', 'max(1000ms, 1s)');
+test_valid_time('max(1s, 1000ms)', 'max(1s, 1000ms)');
+
+test_valid_time('calc(min(1s) + min(2s))', 'calc(min(1s) + min(2s))');
+test_valid_time('calc(min(2s) + min(1s))', 'calc(min(2s) + min(1s))');
+test_valid_time('calc(max(1s) + max(2s))', 'calc(max(1s) + max(2s))');
+test_valid_time('calc(max(2s) + max(1s))', 'calc(max(2s) + max(1s))');
+
+test_valid_time('calc(1s + min(2s))', 'calc(1s + min(2s))');
+test_valid_time('calc(min(2s) + 1s)', 'calc(1s + min(2s))');
+test_valid_time('calc(1s + max(2s))', 'calc(1s + max(2s))');
+test_valid_time('calc(max(2s) + 1s)', 'calc(1s + max(2s))');
+
+</script>
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/resources/mock-snav-service.js b/third_party/blink/web_tests/fast/spatial-navigation/resources/mock-snav-service.js
index 4808163..a504c3d 100644
--- a/third_party/blink/web_tests/fast/spatial-navigation/resources/mock-snav-service.js
+++ b/third_party/blink/web_tests/fast/spatial-navigation/resources/mock-snav-service.js
@@ -10,7 +10,7 @@
     this.callback = null;
     this.bindingSet_ = new mojo.BindingSet(blink.mojom.SpatialNavigationHost);
     this.interceptor_ = new MojoInterfaceInterceptor(
-        blink.mojom.SpatialNavigationHost.name);
+        blink.mojom.SpatialNavigationHost.name, "context", true);
     this.interceptor_.oninterfacerequest =
         e => this.bindingSet_.addBinding(this, e.handle);
     this.interceptor_.start();
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-multiple-select-options.html b/third_party/blink/web_tests/fast/spatial-navigation/snav-multiple-select-options.html
new file mode 100644
index 0000000..b2573db
--- /dev/null
+++ b/third_party/blink/web_tests/fast/spatial-navigation/snav-multiple-select-options.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<!--
+    This test ensures the operation of selecting on multiple select element on Spatial Navigation (SNav) mode.
+
+    * Pre-conditions:
+    1) DRT support for SNav enable/disable.
+
+    * Navigation steps:
+    1) Loads this page, focus goes to "start" automatically.
+    2) Some options are selected by Up/Down and space-bar key.
+  -->
+
+<select id="start" multiple>
+  <option>1</option>
+  <option>2</option>
+  <option disabled>3</option>
+  <option>4</option>
+</select>
+
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+
+<script>
+  snav.assertSnavEnabledAndTestable();
+  test(() => {
+    const start = document.getElementById("start");
+    const options = start.options;
+    start.focus();
+
+    assert_false(options[0].selected, "options[0].selected should be false.");
+    assert_false(options[1].selected, "options[1].selected should be false.");
+    assert_false(options[2].selected, "options[2].selected should be false.");
+    assert_false(options[3].selected, "options[3].selected should be false.");
+
+    eventSender.keyDown(" ");                         //select 1st item
+    assert_true(options[0].selected, "options[0].selected should be true.");
+    assert_false(options[1].selected, "options[1].selected should be false.");
+    assert_false(options[2].selected, "options[2].selected should be false.");
+    assert_false(options[3].selected, "options[3].selected should be false.");
+
+    eventSender.keyDown(" ");                         //deselect 1st item
+    assert_false(options[0].selected, "options[0].selected should be false.");
+    assert_false(options[1].selected, "options[1].selected should be false.");
+    assert_false(options[2].selected, "options[2].selected should be false.");
+    assert_false(options[3].selected, "options[3].selected should be false.");
+
+    eventSender.keyDown("ArrowDown");                 //move to 2nd item
+    assert_false(options[0].selected, "options[0].selected should be false.");
+    assert_false(options[1].selected, "options[1].selected should be false.");
+    assert_false(options[2].selected, "options[2].selected should be false.");
+    assert_false(options[3].selected, "options[3].selected should be false.");
+
+    eventSender.keyDown(" ");                         //select 2nd item
+    assert_false(options[0].selected, "options[0].selected should be false.");
+    assert_true(options[1].selected, "options[1].selected should be true.");
+    assert_false(options[2].selected, "options[2].selected should be false.");
+    assert_false(options[3].selected, "options[3].selected should be false.");
+
+    eventSender.keyDown("ArrowDown");                 //move to 4th item (3rd item is disabled)
+    assert_false(options[0].selected, "options[0].selected should be false.");
+    assert_true(options[1].selected, "options[1].selected should be true.");
+    assert_false(options[2].selected, "options[2].selected should be false.");
+    assert_false(options[3].selected, "options[3].selected should be false.");
+
+    eventSender.keyDown(" ");                         //select 4th item
+    assert_false(options[0].selected, "options[0].selected should be false.");
+    assert_true(options[1].selected, "options[1].selected should be true.");
+    assert_false(options[2].selected, "options[2].selected should be false.");
+    assert_true(options[3].selected, "options[3].selected should be true.");
+
+    eventSender.keyDown("ArrowUp");                   //move back to 2nd item
+    assert_false(options[0].selected, "options[0].selected should be false.");
+    assert_true(options[1].selected, "options[1].selected should be true.");
+    assert_false(options[2].selected, "options[2].selected should be false.");
+    assert_true(options[3].selected, "options[3].selected should be true.");
+
+    eventSender.keyDown(" ");                         //deselect 2nd item
+    assert_false(options[0].selected, "options[0].selected should be false.");
+    assert_false(options[1].selected, "options[1].selected should be false.");
+    assert_false(options[2].selected, "options[2].selected should be false.");
+    assert_true(options[3].selected, "options[3].selected should be true.");
+
+    eventSender.keyDown("ArrowUp");                   //move back to 1st item
+    assert_false(options[0].selected, "options[0].selected should be false.");
+    assert_false(options[1].selected, "options[1].selected should be false.");
+    assert_false(options[2].selected, "options[2].selected should be false.");
+    assert_true(options[3].selected, "options[3].selected should be true.");
+
+    eventSender.keyDown("ArrowDown", ["shiftKey"]);   //shift-down to 2nd item
+    assert_false(options[0].selected, "options[0].selected should be false.");
+    assert_true(options[1].selected, "options[1].selected should be true.");
+    assert_false(options[2].selected, "options[2].selected should be false.");
+    assert_true(options[3].selected, "options[3].selected should be true.");
+  });
+</script>
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/external/wpt/css/css-grid/grid-items/grid-item-percentage-sizes-003-expected.png b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/external/wpt/css/css-grid/grid-items/grid-item-percentage-sizes-003-expected.png
deleted file mode 100644
index 4c97903a..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/external/wpt/css/css-grid/grid-items/grid-item-percentage-sizes-003-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/performance/performance-pane-a11y-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/performance/performance-pane-a11y-test-expected.txt
new file mode 100644
index 0000000..8864297
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/performance/performance-pane-a11y-test-expected.txt
@@ -0,0 +1,16 @@
+
+Running: testDetailsView
+Tests accessibility in performance Details view using the axe-core linter
+aXe violations: []
+
+
+Running: testBottomUpView
+Tests accessibility in performance BottomUp view using the axe-core linter
+aXe violations: []
+
+
+Running: testCallTreeView
+Tests accessibility in performance CallTree view using the axe-core linter
+aXe violations: []
+
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/performance/performance-pane-a11y-test.js b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/performance/performance-pane-a11y-test.js
new file mode 100644
index 0000000..875581f
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/performance/performance-pane-a11y-test.js
@@ -0,0 +1,82 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+(async function() {
+  await TestRunner.loadModule('axe_core_test_runner');
+  await TestRunner.loadModule('performance_test_runner');
+  await TestRunner.showPanel('timeline');
+
+  const testData = [
+    {
+      'name': 'top level event name',
+      'ts': 1000000,
+      'ph': 'B',
+      'tid': 1,
+      'pid': 100,
+      'cat': 'toplevel',
+      'args': {'data': {'message': 'AAA'}}
+    },
+    {
+      'name': 'TimeStamp',
+      'ts': 1010000,
+      'ph': 'B',
+      'tid': 1,
+      'pid': 100,
+      'cat': 'toplevel',
+      'args': {'data': {'message': 'BBB'}}
+    },
+    {
+      'name': 'TimeStamp',
+      'ts': 1020000,
+      'ph': 'B',
+      'tid': 1,
+      'pid': 100,
+      'cat': 'toplevel',
+      'args': {'data': {'message': 'CCC'}}
+    },
+  ];
+
+  // create dummy data for test
+  const model =
+      PerformanceTestRunner.createPerformanceModelWithEvents(testData);
+  const detailsView = runtime.sharedInstance(Timeline.TimelineDetailsView);
+
+  async function testDetailsView() {
+    TestRunner.addResult(`Tests accessibility in performance Details view using the axe-core linter`);
+    detailsView._tabbedPane.selectTab(Timeline.TimelineDetailsView.Tab.Details);
+    const detailsTab = detailsView._tabbedPane.visibleView;
+
+    // Details pane gets data from the parent TimelineDetails view
+    detailsView.setModel(model, PerformanceTestRunner.mainTrack());
+
+    await AxeCoreTestRunner.runValidation(detailsTab.element);
+  }
+
+  async function testViewWithName(tab) {
+    TestRunner.addResult(`Tests accessibility in performance ${tab} view using the axe-core linter`);
+    detailsView._tabbedPane.selectTab(tab);
+    const detailsTab = detailsView._tabbedPane.visibleView;
+
+    // update child views with the same test data
+    detailsTab.setModel(model, PerformanceTestRunner.mainTrack());
+    detailsTab.updateContents(Timeline.TimelineSelection.fromRange(
+        model.timelineModel().minimumRecordTime(),
+        model.timelineModel().maximumRecordTime()));
+
+    await AxeCoreTestRunner.runValidation(detailsTab.element);
+  }
+
+  function testBottomUpView() {
+    return testViewWithName(Timeline.TimelineDetailsView.Tab.BottomUp);
+  }
+
+  function testCallTreeView() {
+    return testViewWithName(Timeline.TimelineDetailsView.Tab.CallTree);
+  }
+
+  TestRunner.runAsyncTestSuite([
+    testDetailsView,
+    testBottomUpView,
+    testCallTreeView,
+  ]);
+})();
\ No newline at end of file
diff --git a/third_party/blink/web_tests/media/picture-in-picture/picture-in-picture-service-error.html b/third_party/blink/web_tests/media/picture-in-picture/picture-in-picture-service-error.html
index 9f4247e..24291e8b 100644
--- a/third_party/blink/web_tests/media/picture-in-picture/picture-in-picture-service-error.html
+++ b/third_party/blink/web_tests/media/picture-in-picture/picture-in-picture-service-error.html
@@ -15,7 +15,7 @@
         blink.mojom.PictureInPictureService);
 
     this.interceptor_ =
-        new MojoInterfaceInterceptor(blink.mojom.PictureInPictureService.name);
+        new MojoInterfaceInterceptor(blink.mojom.PictureInPictureService.name, "context", true);
     this.interceptor_.oninterfacerequest =
         e => this.bindingSet_.addBinding(this, e.handle);
     this.interceptor_.start();
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/css/css-text/text-align/text-align-last-empty-inline-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-text/text-align/text-align-last-empty-inline-expected.txt
new file mode 100644
index 0000000..7beafc77
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-text/text-align/text-align-last-empty-inline-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL content that cannot be justified should be centered when text-align-last is justify assert_equals: expected 395.4375 but got 8
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/css/css-text/text-align/text-align-last-empty-inline-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/css/css-text/text-align/text-align-last-empty-inline-expected.txt
new file mode 100644
index 0000000..5899e01
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/external/wpt/css/css-text/text-align/text-align-last-empty-inline-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL content that cannot be justified should be centered when text-align-last is justify assert_equals: expected 395.5 but got 8
+Harness: the test ran to completion.
+
diff --git a/third_party/webrtc_overrides/OWNERS b/third_party/webrtc_overrides/OWNERS
index 79bef8ef..5f3540d 100644
--- a/third_party/webrtc_overrides/OWNERS
+++ b/third_party/webrtc_overrides/OWNERS
@@ -1,5 +1,5 @@
-ajm@chromium.org
 grunell@chromium.org
+mbonadei@chromium.org
 tommi@chromium.org
 
 # COMPONENT: Blink>WebRTC
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 3f2d85a..3a6262b 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -1597,7 +1597,7 @@
 
     'gpu_tests_android_release_trybot_arm64_resource_whitelisting_java_coverage': [
       'android', 'release_trybot', 'arm64', 'static_angle', 'resource_whitelisting',
-      'java_coverage',
+      'java_coverage', 'partial_code_coverage_instrumentation',
     ],
 
     'gpu_tests_android_vulkan_release_trybot': [
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 4726699..b8f7786 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -61518,6 +61518,10 @@
   <int value="3" label="FAILED_OTHER">Failed for other reason</int>
 </enum>
 
+<enum name="VAIPFailure">
+  <int value="0" label="VAAPI_VPP_ERROR"/>
+</enum>
+
 <enum name="VAJDAFailure">
   <int value="0" label="VAAPI_ERROR"/>
 </enum>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 7f4582e..6a21df5 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -63661,6 +63661,16 @@
   </summary>
 </histogram>
 
+<histogram name="Media.VAIP.VppFailure" enum="VAIPFailure"
+    expires_after="2021-01-01">
+  <owner>hiroh@chromium.org</owner>
+  <owner>chromeos-gfx@chromium.org</owner>
+  <summary>
+    Count of VAAPI errors that occur inside the VaapiWrapper due to VPP
+    functionality needed by the VaapiImageProcessor.
+  </summary>
+</histogram>
+
 <histogram name="Media.VAJDA.DecoderFailure" enum="VAJDAFailure">
   <owner>andrescj@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
diff --git a/tools/perf/benchmarks/system_health_smoke_test.py b/tools/perf/benchmarks/system_health_smoke_test.py
index 607eb2e..4ddb9ad 100644
--- a/tools/perf/benchmarks/system_health_smoke_test.py
+++ b/tools/perf/benchmarks/system_health_smoke_test.py
@@ -46,6 +46,7 @@
   'system_health.memory_mobile/browse:social:tumblr_infinite_scroll',
   'system_health.memory_mobile/browse:tools:maps',
   'system_health.memory_mobile/browse:news:cnn',
+  'system_health.memory_mobile/browse:news:washingtonpost',
   'system_health.memory_mobile/load:media:facebook_photos',
   'system_health.memory_mobile/load:news:cnn',
   'system_health.memory_mobile/load:news:nytimes',
diff --git a/tools/perf/page_sets/data/system_health_mobile.json b/tools/perf/page_sets/data/system_health_mobile.json
index 90419ab5..e8bdb88 100644
--- a/tools/perf/page_sets/data/system_health_mobile.json
+++ b/tools/perf/page_sets/data/system_health_mobile.json
@@ -72,6 +72,9 @@
         "browse:news:washingtonpost": {
             "DEFAULT": "system_health_mobile_021.wprgo"
         },
+        "browse:news:washingtonpost:2019": {
+            "DEFAULT": "system_health_mobile_93ff1080a1.wprgo"
+        },
         "browse:search:amp:2018": {
             "DEFAULT": "system_health_mobile_0483ae239d.wprgo"
         },
diff --git a/tools/perf/page_sets/data/system_health_mobile_93ff1080a1.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_93ff1080a1.wprgo.sha1
new file mode 100644
index 0000000..a683a4c7
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_mobile_93ff1080a1.wprgo.sha1
@@ -0,0 +1 @@
+0acd8c8e562da7066994efde8411a477608a9362
\ No newline at end of file
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index c44d81a..8ba87ce 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -304,6 +304,29 @@
     super(WashingtonPostMobileStory, self)._DidLoadDocument(action_runner)
 
 
+class WashingtonPostMobileStory2019(_ArticleBrowsingStory):
+  """Progressive website"""
+  NAME = 'browse:news:washingtonpost:2019'
+  URL = 'https://www.washingtonpost.com/pwa'
+  IS_SINGLE_PAGE_APP = True
+  ITEM_SELECTOR = '.headline > a'
+  SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
+  _BROWSE_FREE_SELECTOR = '[class="continue-btn button free"]'
+  _I_AGREE_SELECTOR = '.agree-ckb'
+  _CONTINUE_SELECTOR = '[class="continue-btn button accept-consent"]'
+  TAGS = [story_tags.YEAR_2019]
+
+  def _DidLoadDocument(self, action_runner):
+    # Get past GDPR and subscription dialog.
+    action_runner.WaitForElement(selector=self._BROWSE_FREE_SELECTOR)
+    action_runner.ClickElement(selector=self._BROWSE_FREE_SELECTOR)
+    action_runner.WaitForElement(selector=self._I_AGREE_SELECTOR)
+    action_runner.ClickElement(selector=self._I_AGREE_SELECTOR)
+    action_runner.ClickElement(selector=self._CONTINUE_SELECTOR)
+
+    super(WashingtonPostMobileStory2019, self)._DidLoadDocument(action_runner)
+
+
 ##############################################################################
 # Search browsing stories.
 ##############################################################################
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index ca0179c..ff3692c 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -162,6 +162,8 @@
     public += [ "screen_ozone.h" ]
     sources += [ "screen_ozone.cc" ]
   }
+
+  configs += [ "//build/config/compiler:noshadowing" ]
 }
 
 jumbo_static_library("test_support") {
diff --git a/ui/aura/window_event_dispatcher.cc b/ui/aura/window_event_dispatcher.cc
index 07db9b12..aaf511d 100644
--- a/ui/aura/window_event_dispatcher.cc
+++ b/ui/aura/window_event_dispatcher.cc
@@ -166,7 +166,7 @@
   Window* target = ConsumerToWindow(raw_input_consumer);
   if (target) {
     event->ConvertLocationToTarget(window(), target);
-    DispatchDetails details = DispatchEvent(target, event);
+    details = DispatchEvent(target, event);
     if (details.dispatcher_destroyed)
       return;
   }
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index 7e0e7ae..7ae3b92 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -1083,27 +1083,6 @@
   <message name="IDS_FILE_BROWSER_OPEN_IN_OTHER_DESKTOP_MESSAGE_PLURAL" desc="Prural verson of the message of an alert dialog that is shown when a user opens a file in a desktop of another profile but the file is opened in the original desktop.">
     These files opened on a different desktop. Move to <ph name="USER_NAME">$1<ex>John Doe</ex></ph> (<ph name="MAIL_ADDRESS">$2<ex>xxxxxx@gmail.com</ex></ph>) to view it.
   </message>
-  <message name="IDS_FILE_BROWSER_MULTI_PROFILE_SHARE_DIALOG_TITLE" desc="Title of a dialog to request drive share for files that come from other profiles during file transfering.">
-    This Drive file isn't shared yet
-  </message>
-  <message name="IDS_FILE_BROWSER_MULTI_PROFILE_SHARE_DIALOG_TITLE_PLURAL" desc="Title of a dialog to request drive share for files that come from other profiles during file transfering.">
-    These Drive files aren't shared yet
-  </message>
-  <message name="IDS_FILE_BROWSER_MULTI_PROFILE_SHARE_DIALOG_MESSAGE" desc="Message of a dialog to request drive share for files that come from other profiles during file transfering. Below the message, there are the e-mail address of the account to be shared with the file, and a drop down list to select a share type.">
-    Change how this file is shared
-  </message>
-  <message name="IDS_FILE_BROWSER_MULTI_PROFILE_SHARE_DIALOG_MESSAGE_PLURAL" desc="Message of a dialog to request drive share for files that come from other profiles during file transfering. Below the message, there are the e-mail address of the account to be shared with the file, and a drop down list to select a share type.">
-    Change how these files are shared
-  </message>
-  <message name="IDS_FILE_BROWSER_DRIVE_SHARE_TYPE_CAN_EDIT" desc="Label text of 'Can edit' share type. Please use the same string with Google Drive.">
-    Can edit
-  </message>
-  <message name="IDS_FILE_BROWSER_DRIVE_SHARE_TYPE_CAN_COMMENT" desc="Label text of 'Can comment' share type. Please use the same string with Google Drive.">
-    Can comment
-  </message>
-  <message name="IDS_FILE_BROWSER_DRIVE_SHARE_TYPE_CAN_VIEW" desc="Label text of 'Can view' share type. Please use the same string with Google Drive.">
-    Can view
-  </message>
   <message name="IDS_FILE_BROWSER_DRIVE_MOBILE_CONNECTION_OPTION" desc="Option that determines if we will allow Google Drive content sync over mobile connections.">
     Do not use mobile data for sync
   </message>
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index 5401b33..62a31914 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -438,7 +438,6 @@
     "ui:directory_tree",
     "ui:file_manager_ui",
     "ui:list_container",
-    "ui:multi_profile_share_dialog",
     "ui:progress_center_panel",
     "//ui/file_manager/file_manager/common/js:progress_center_common",
   ]
diff --git a/ui/file_manager/file_manager/foreground/js/dialog_action_controller.js b/ui/file_manager/file_manager/foreground/js/dialog_action_controller.js
index 858a44c..d419a060 100644
--- a/ui/file_manager/file_manager/foreground/js/dialog_action_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/dialog_action_controller.js
@@ -245,133 +245,30 @@
    */
   selectFilesAndClose_(selection) {
     const currentRootType = this.directoryModel_.getCurrentRootType();
-    const callSelectFilesApiAndClose = callback => {
-      const onFileSelected = () => {
-        callback();
-        if (!chrome.runtime.lastError) {
-          // Call next method on a timeout, as it's unsafe to
-          // close a window from a callback.
-          setTimeout(window.close.bind(window), 0);
-        }
-      };
-      // Record the root types of chosen files in OPEN dialog.
-      if (this.dialogType_ == DialogType.SELECT_OPEN_FILE ||
-          this.dialogType_ == DialogType.SELECT_OPEN_MULTI_FILE) {
-        metrics.recordEnum(
-            'OpenFiles.RootType', currentRootType,
-            VolumeManagerCommon.RootTypesForUMA);
-      }
-      if (selection.multiple) {
-        chrome.fileManagerPrivate.selectFiles(
-            selection.urls, this.allowedPaths_ === AllowedPaths.NATIVE_PATH,
-            onFileSelected);
-      } else {
-        chrome.fileManagerPrivate.selectFile(
-            selection.urls[0], selection.filterIndex,
-            this.dialogType_ !==
-                DialogType.SELECT_SAVEAS_FILE /* for opening */,
-            this.allowedPaths_ === AllowedPaths.NATIVE_PATH, onFileSelected);
+    const onFileSelected = () => {
+      if (!chrome.runtime.lastError) {
+        // Call next method on a timeout, as it's unsafe to
+        // close a window from a callback.
+        setTimeout(window.close.bind(window), 0);
       }
     };
-
-    if (currentRootType !== VolumeManagerCommon.VolumeType.DRIVE ||
-        this.dialogType_ === DialogType.SELECT_SAVEAS_FILE) {
-      callSelectFilesApiAndClose(() => {});
-      return;
+    // Record the root types of chosen files in OPEN dialog.
+    if (this.dialogType_ == DialogType.SELECT_OPEN_FILE ||
+        this.dialogType_ == DialogType.SELECT_OPEN_MULTI_FILE) {
+      metrics.recordEnum(
+          'OpenFiles.RootType', currentRootType,
+          VolumeManagerCommon.RootTypesForUMA);
     }
-
-    const shade = document.createElement('div');
-    shade.className = 'shade';
-    const footer = this.dialogFooter_.element;
-    const progress = footer.querySelector('.progress-track');
-    progress.style.width = '0%';
-    const cancelled = false;
-
-    const progressMap = {};
-    let filesStarted = 0;
-    let filesTotal = selection.urls.length;
-    for (let index = 0; index < selection.urls.length; index++) {
-      progressMap[selection.urls[index]] = -1;
+    if (selection.multiple) {
+      chrome.fileManagerPrivate.selectFiles(
+          selection.urls, this.allowedPaths_ === AllowedPaths.NATIVE_PATH,
+          onFileSelected);
+    } else {
+      chrome.fileManagerPrivate.selectFile(
+          selection.urls[0], selection.filterIndex,
+          this.dialogType_ !== DialogType.SELECT_SAVEAS_FILE /* for opening */,
+          this.allowedPaths_ === AllowedPaths.NATIVE_PATH, onFileSelected);
     }
-    let lastPercent = 0;
-    let bytesTotal = 0;
-    let bytesDone = 0;
-
-    const onFileTransfersUpdated = status => {
-      if (!(status.fileUrl in progressMap)) {
-        return;
-      }
-      if (status.total === -1) {
-        return;
-      }
-
-      let old = progressMap[status.fileUrl];
-      if (old === -1) {
-        // -1 means we don't know file size yet.
-        bytesTotal += status.total;
-        filesStarted++;
-        old = 0;
-      }
-      bytesDone += status.processed - old;
-      progressMap[status.fileUrl] = status.processed;
-
-      let percent = bytesTotal === 0 ? 0 : bytesDone / bytesTotal;
-      // For files we don't have information about, assume the progress is zero.
-      percent = percent * filesStarted / filesTotal * 100;
-      // Do not decrease the progress. This may happen, if first downloaded
-      // file is small, and the second one is large.
-      lastPercent = Math.max(lastPercent, percent);
-      progress.style.width = lastPercent + '%';
-    };
-
-    const setup = () => {
-      document.querySelector('.dialog-container').appendChild(shade);
-      setTimeout(() => {
-        shade.setAttribute('fadein', 'fadein');
-      }, 100);
-      footer.setAttribute('progress', 'progress');
-      this.dialogFooter_.cancelButton.removeEventListener(
-          'click', this.onCancelBound_);
-      this.dialogFooter_.cancelButton.addEventListener('click', onCancel);
-      chrome.fileManagerPrivate.onFileTransfersUpdated.addListener(
-          onFileTransfersUpdated);
-    };
-
-    const cleanup = () => {
-      shade.parentNode.removeChild(shade);
-      footer.removeAttribute('progress');
-      this.dialogFooter_.cancelButton.removeEventListener('click', onCancel);
-      this.dialogFooter_.cancelButton.addEventListener(
-          'click', this.onCancelBound_);
-      chrome.fileManagerPrivate.onFileTransfersUpdated.removeListener(
-          onFileTransfersUpdated);
-    };
-
-    const onCancel = () => {
-      // According to API cancel may fail, but there is no proper UI to reflect
-      // this. So, we just silently assume that everything is cancelled.
-      util.URLsToEntries(selection.urls).then(entries => {
-        chrome.fileManagerPrivate.cancelFileTransfers(
-            entries, util.checkAPIError);
-      });
-    };
-
-    const onProperties = properties => {
-      for (let i = 0; i < properties.length; i++) {
-        if (properties[i].present) {
-          // For files already in GCache, we don't get any transfer updates.
-          filesTotal--;
-        }
-      }
-      callSelectFilesApiAndClose(cleanup);
-    };
-
-    setup();
-
-    // TODO(mtomasz): Use Entry instead of URLs, if possible.
-    util.URLsToEntries(selection.urls, entries => {
-      this.metadataModel_.get(entries, ['present']).then(onProperties);
-    });
   }
 
   /**
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 25a04b6a..4896230f 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -642,7 +642,7 @@
 
     this.fileTransferController_ = new FileTransferController(
         assert(this.document_), assert(this.ui_.listContainer),
-        assert(this.ui_.directoryTree), this.ui_.multiProfileShareDialog,
+        assert(this.ui_.directoryTree),
         this.ui_.showConfirmationDialog.bind(this.ui_),
         assert(this.fileBrowserBackground_.progressCenter),
         assert(this.fileOperationManager_), assert(this.metadataModel_),
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
index 1f5a9a9..688a2e2 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -20,8 +20,6 @@
    * @param {!Document} doc Owning document.
    * @param {!ListContainer} listContainer List container.
    * @param {!DirectoryTree} directoryTree Directory tree.
-   * @param {!MultiProfileShareDialog} multiProfileShareDialog Share dialog to
-   *     be used to share files from another profile.
    * @param {function(boolean, !Array<string>): !Promise<boolean>}
    *     confirmationCallback called when operation requires user's
    *     confirmation. The operation will be executed if the return value
@@ -36,9 +34,9 @@
    * @param {!FileSelectionHandler} selectionHandler Selection handler.
    */
   constructor(
-      doc, listContainer, directoryTree, multiProfileShareDialog,
-      confirmationCallback, progressCenter, fileOperationManager, metadataModel,
-      thumbnailModel, directoryModel, volumeManager, selectionHandler) {
+      doc, listContainer, directoryTree, confirmationCallback, progressCenter,
+      fileOperationManager, metadataModel, thumbnailModel, directoryModel,
+      volumeManager, selectionHandler) {
     /**
      * @private {!Document}
      * @const
@@ -88,12 +86,6 @@
     this.selectionHandler_ = selectionHandler;
 
     /**
-     * @private {!MultiProfileShareDialog}
-     * @const
-     */
-    this.multiProfileShareDialog_ = multiProfileShareDialog;
-
-    /**
      * @private {function(boolean, !Array<string>):
      *     Promise<boolean>}
      * @const
@@ -414,84 +406,6 @@
   }
 
   /**
-   * Obtains entries that need to share with me.
-   * The method also observers child entries of the given entries.
-   * @param {Array<Entry>} entries Entries.
-   * @return {!Promise<Array<Entry>>} Promise to be fulfilled with the entries
-   *    that need to share.
-   * @private
-   */
-  getMultiProfileShareEntries_(entries) {
-    // Utility function to concat arrays.
-    const concatArrays = arrays => {
-      return Array.prototype.concat.apply([], arrays);
-    };
-
-    // Call processEntry for each item of entries.
-    const processEntries = entries => {
-      const files = entries.filter(entry => {
-        return entry.isFile;
-      });
-      const dirs = entries.filter(entry => {
-        return !entry.isFile;
-      });
-      const promises = dirs.map(processDirectoryEntry);
-      if (files.length > 0) {
-        promises.push(processFileEntries(files));
-      }
-      return Promise.all(promises).then(concatArrays);
-    };
-
-    // Check all file entries and keeps only those need sharing operation.
-    const processFileEntries = entries => {
-      return new Promise(callback => {
-               // Do not use metadata cache here because the urls come from the
-               // different profile.
-               chrome.fileManagerPrivate.getEntryProperties(
-                   entries, ['hosted', 'sharedWithMe'], callback);
-             })
-          .then(metadatas => {
-            return entries.filter((entry, i) => {
-              const metadata = metadatas[i];
-              return metadata && metadata.hosted && !metadata.sharedWithMe;
-            });
-          });
-    };
-
-    // Check child entries.
-    const processDirectoryEntry = entry => {
-      return readEntries(entry.createReader());
-    };
-
-    // Read entries from DirectoryReader and call processEntries for the chunk
-    // of entries.
-    const readEntries = reader => {
-      return new Promise(reader.readEntries.bind(reader))
-          .then(
-              entries => {
-                if (entries.length > 0) {
-                  return Promise
-                      .all([processEntries(entries), readEntries(reader)])
-                      .then(concatArrays);
-                } else {
-                  return [];
-                }
-              },
-              error => {
-                console.warn('Error happens while reading directory.', error);
-                return [];
-              });
-    };
-
-    // Filter entries that is owned by the current user, and call
-    // processEntries.
-    return processEntries(entries.filter(entry => {
-      // If the volumeInfo is found, the entry belongs to the current user.
-      return !this.volumeManager_.getVolumeInfo(/** @type {!Entry} */ (entry));
-    }));
-  }
-
-  /**
    * Collects parameters of paste operation by the given command and the current
    * system clipboard.
    *
@@ -619,7 +533,6 @@
               })
         .then(/**
                * @param {!Array<Entry>} filteredEntries
-               * @return {!Promise<Array<Entry>>}
                */
               filteredEntries => {
                 entries = filteredEntries;
@@ -661,56 +574,12 @@
                 item.subMessage =
                     strf('TO_FOLDER_NAME', item.destinationMessage);
                 this.progressCenter_.updateItem(item);
-                // Check if cross share is needed or not.
-                return this.getMultiProfileShareEntries_(entries);
-              })
-        .then(/**
-               * @param {Array<Entry>} inShareEntries
-               * @return {!Promise<Array<Entry>>|!Promise<null>}
-               */
-              inShareEntries => {
-                shareEntries = inShareEntries;
-                if (shareEntries.length === 0) {
-                  return Promise.resolve(null);
-                }
-                return this.multiProfileShareDialog_
-                    .showMultiProfileShareDialog(shareEntries.length > 1);
-              })
-        .then(
-            /**
-             * @param {?string} dialogResult
-             * @return {!Promise<undefined>|undefined}
-             */
-            dialogResult => {
-              if (dialogResult === null) {
-                return;
-              }  // No dialog was shown, skip this step.
-              if (dialogResult === 'cancel') {
-                return Promise.reject('ABORT');
-              }
-              // Do cross share.
-              // TODO(hirono): Make the loop cancellable.
-              const requestDriveShare = index => {
-                if (index >= shareEntries.length) {
-                  return;
-                }
-                return new Promise(fulfill => {
-                         chrome.fileManagerPrivate.requestDriveShare(
-                             shareEntries[index], assert(dialogResult), () => {
-                               // TODO(hirono): Check chrome.runtime.lastError
-                               // here.
-                               fulfill();
-                             });
-                       })
-                    .then(requestDriveShare.bind(null, index + 1));
-              };
-              return requestDriveShare(0);
-            })
-        .then(() => {
-          // Start the pasting operation.
-          this.fileOperationManager_.paste(
-              entries, destinationEntry, toMove, taskId);
-          this.pendingTaskIds.splice(this.pendingTaskIds.indexOf(taskId), 1);
+
+                // Start the pasting operation.
+                this.fileOperationManager_.paste(
+                    entries, destinationEntry, toMove, taskId);
+                this.pendingTaskIds.splice(
+                    this.pendingTaskIds.indexOf(taskId), 1);
         })
         .catch(error => {
           if (error !== 'ABORT') {
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js
index 92310a3..8f0e4b0 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js
@@ -66,10 +66,6 @@
   // Initialize cr.ui.Command with the <command>s.
   cr.ui.decorate('command', cr.ui.Command);
 
-  // Setup MultiProfileShareDialog.
-  const multiProfileShareDialog =
-      new MultiProfileShareDialog(queryRequiredElement('#dialog'));
-
   // Fake confirmation callback.
   const confirmationDialog = (isMove, messages) => Promise.resolve(true);
 
@@ -139,7 +135,6 @@
       document,
       listContainer,
       directoryTree,
-      multiProfileShareDialog,
       confirmationDialog,
       progressCenter,
       fileOperationManager,
diff --git a/ui/file_manager/file_manager/foreground/js/main_scripts.js b/ui/file_manager/file_manager/foreground/js/main_scripts.js
index 19675f2..acb7e0d1 100644
--- a/ui/file_manager/file_manager/foreground/js/main_scripts.js
+++ b/ui/file_manager/file_manager/foreground/js/main_scripts.js
@@ -173,7 +173,6 @@
 // <include src="ui/import_crostini_image_dialog.js">
 // <include src="ui/list_container.js">
 // <include src="ui/location_line.js">
-// <include src="ui/multi_profile_share_dialog.js">
 // <include src="ui/multi_menu.js">
 // <include src="ui/progress_center_panel.js">
 // <include src="ui/providers_menu.js">
diff --git a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
index 0b64eb8..9f58268 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
@@ -47,7 +47,6 @@
     ":list_container",
     ":location_line",
     ":multi_menu",
-    ":multi_profile_share_dialog",
     ":progress_center_panel",
     ":providers_menu",
     ":search_box",
@@ -257,7 +256,6 @@
     ":list_container",
     ":location_line",
     ":multi_menu",
-    ":multi_profile_share_dialog",
     ":progress_center_panel",
     ":providers_menu",
     ":search_box",
@@ -428,13 +426,6 @@
   ]
 }
 
-js_library("multi_profile_share_dialog") {
-  deps = [
-    ":file_manager_dialog_base",
-    "//ui/file_manager/file_manager/common/js:util",
-  ]
-}
-
 js_library("progress_center_panel") {
   # The progress_center on the background page maintains a list of panels.
   visibility += [ "//ui/file_manager/file_manager/background/*" ]
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
index badef37..778565b 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
@@ -81,13 +81,6 @@
     this.copyConfirmDialog.setOkLabel(str('CONFIRM_COPY_BUTTON_LABEL'));
 
     /**
-     * Multi-profile share dialog.
-     * @type {!MultiProfileShareDialog}
-     * @const
-     */
-    this.multiProfileShareDialog = new MultiProfileShareDialog(this.element);
-
-    /**
      * Default task picker.
      * @type {!cr.filebrowser.DefaultTaskDialog}
      * @const
diff --git a/ui/file_manager/file_manager/foreground/js/ui/multi_profile_share_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/multi_profile_share_dialog.js
deleted file mode 100644
index 22fe681..0000000
--- a/ui/file_manager/file_manager/foreground/js/ui/multi_profile_share_dialog.js
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * Dialog to confirm the share between profiles.
- *
- */
-class MultiProfileShareDialog extends FileManagerDialogBase {
-  /**
-   * @param {HTMLElement} parentNode Node to be parent for this dialog.
-   */
-  constructor(parentNode) {
-    super(parentNode);
-
-    this.mailLabel_ = parentNode.ownerDocument.createElement('label');
-    this.mailLabel_.className = 'mail-label';
-
-    const canEdit = parentNode.ownerDocument.createElement('option');
-    canEdit.textContent = str('DRIVE_SHARE_TYPE_CAN_EDIT');
-    canEdit.value = MultiProfileShareDialog.Result.CAN_EDIT;
-
-    const canComment = parentNode.ownerDocument.createElement('option');
-    canComment.textContent = str('DRIVE_SHARE_TYPE_CAN_COMMENT');
-    canComment.value = MultiProfileShareDialog.Result.CAN_COMMET;
-
-    const canView = parentNode.ownerDocument.createElement('option');
-    canView.textContent = str('DRIVE_SHARE_TYPE_CAN_VIEW');
-    canView.value = MultiProfileShareDialog.Result.CAN_VIEW;
-
-    this.shareTypeSelect_ = parentNode.ownerDocument.createElement('select');
-    this.shareTypeSelect_.setAttribute('size', 1);
-    this.shareTypeSelect_.appendChild(canEdit);
-    this.shareTypeSelect_.appendChild(canComment);
-    this.shareTypeSelect_.appendChild(canView);
-
-    const shareLine = parentNode.ownerDocument.createElement('div');
-    shareLine.className = 'share-line';
-    shareLine.appendChild(this.mailLabel_);
-    shareLine.appendChild(this.shareTypeSelect_);
-
-    this.frame.insertBefore(shareLine, this.buttons);
-    this.frame.id = 'multi-profile-share-dialog';
-
-    this.currentProfileId_ = new Promise(callback => {
-      chrome.fileManagerPrivate.getProfiles(
-          (profiles, currentId, displayedId) => {
-            callback(currentId);
-          });
-    });
-  }
-
-  /**
-   * Shows the dialog.
-   * @param {boolean} plural Whether to use message of plural or not.
-   * @return {!Promise} Promise fulfilled with the result of dialog. If the
-   *     dialog is already opened, it returns null.
-   */
-  showMultiProfileShareDialog(plural) {
-    return this.currentProfileId_.then(currentProfileId => {
-      return new Promise((fulfill, reject) => {
-        this.shareTypeSelect_.selectedIndex = 0;
-        this.mailLabel_.textContent = currentProfileId;
-        const result = super.showOkCancelDialog(
-            str(plural ? 'MULTI_PROFILE_SHARE_DIALOG_TITLE_PLURAL' :
-                         'MULTI_PROFILE_SHARE_DIALOG_TITLE'),
-            str(plural ? 'MULTI_PROFILE_SHARE_DIALOG_MESSAGE_PLURAL' :
-                         'MULTI_PROFILE_SHARE_DIALOG_MESSAGE'),
-            () => {
-              fulfill(this.shareTypeSelect_.value);
-            },
-            () => {
-              fulfill(MultiProfileShareDialog.Result.CANCEL);
-            });
-        if (!result) {
-          reject(new Error('Another dialog has already shown.'));
-        }
-      });
-    });
-  }
-}
-
-/**
- * Result of the dialog box.
- * @enum {string}
- * @const
- */
-MultiProfileShareDialog.Result = {
-  CAN_EDIT: 'can_edit',
-  CAN_COMMET: 'can_comment',
-  CAN_VIEW: 'can_view',
-  CANCEL: 'cancel'
-};
-Object.freeze(MultiProfileShareDialog.Result);
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index fad8e4f..2c53b92 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -498,7 +498,10 @@
   ]
 
   sources += get_target_outputs(":views_vector_icons")
-  configs += [ "//build/config:precompiled_headers" ]
+  configs += [
+    "//build/config:precompiled_headers",
+    "//build/config/compiler:noshadowing",
+  ]
   defines = [ "VIEWS_IMPLEMENTATION" ]
 
   deps = [
diff --git a/ui/views/controls/button/label_button_border.cc b/ui/views/controls/button/label_button_border.cc
index 978f6a8..5063dc1 100644
--- a/ui/views/controls/button/label_button_border.cc
+++ b/ui/views/controls/button/label_button_border.cc
@@ -105,7 +105,7 @@
 
     {
       // First, modulate the background by 1 - alpha.
-      cc::PaintCanvasAutoRestore auto_restore(canvas->sk_canvas(), false);
+      cc::PaintCanvasAutoRestore auto_restore_alpha(canvas->sk_canvas(), false);
       canvas->sk_canvas()->saveLayerAlpha(&sk_rect, 255 - fg_alpha);
       state = native_theme_delegate->GetBackgroundThemeState(&extra);
       PaintHelper(this, canvas, state, rect, extra);
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 729e5ec..ed23492 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -180,10 +180,10 @@
     // The first child is taking over, just use its accessible name instead of
     // |title_|.
     View* child = children().front();
-    ui::AXNodeData node_data;
-    child->GetAccessibleNodeData(&node_data);
+    ui::AXNodeData child_node_data;
+    child->GetAccessibleNodeData(&child_node_data);
     item_text =
-        node_data.GetString16Attribute(ax::mojom::StringAttribute::kName);
+        child_node_data.GetString16Attribute(ax::mojom::StringAttribute::kName);
   } else {
     item_text = title_;
   }
@@ -667,7 +667,7 @@
   } else {
     // Child views are laid out right aligned and given the full height. To
     // right align start with the last view and progress to the first.
-    int x = width() - (use_right_margin_ ? item_right_margin_ : 0);
+    int child_x = width() - (use_right_margin_ ? item_right_margin_ : 0);
     for (View* child : base::Reversed(children())) {
       if (icon_view_ == child)
         continue;
@@ -678,8 +678,8 @@
       if (vertical_separator_ == child)
         continue;
       int width = child->GetPreferredSize().width();
-      child->SetBounds(x - width, 0, width, height());
-      x -= width + kChildXPadding;
+      child->SetBounds(child_x - width, 0, width, height());
+      child_x -= width + kChildXPadding;
     }
     // Position |icon_view|.
     const MenuConfig& config = MenuConfig::instance();
diff --git a/ui/views/widget/desktop_aura/desktop_capture_client.cc b/ui/views/widget/desktop_aura/desktop_capture_client.cc
index 7cdc246..4b6586c 100644
--- a/ui/views/widget/desktop_aura/desktop_capture_client.cc
+++ b/ui/views/widget/desktop_aura/desktop_capture_client.cc
@@ -89,9 +89,9 @@
     ClientSet clients(*clients_);
     for (auto client : clients) {
       if (client && client.get() != this) {
-        aura::client::CaptureDelegate* delegate =
+        aura::client::CaptureDelegate* client_delegate =
             client->root_->GetHost()->dispatcher();
-        delegate->OnOtherRootGotCapture();
+        client_delegate->OnOtherRootGotCapture();
       }
     }
   }  // else case is capture is remaining in our root, nothing to do.
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc
index be91014..9912714 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc
@@ -674,22 +674,22 @@
           std::make_unique<ui::OSExchangeDataProviderAuraX11>(
               xwindow_, target_current_context_->fetched_targets())));
 
-      ui::DropTargetEvent event(*data.get(),
-                                gfx::PointF(target_window_location_),
-                                gfx::PointF(target_window_root_location_),
-                                target_current_context_->GetDragOperation());
+      ui::DropTargetEvent drop_event(
+          *data.get(), gfx::PointF(target_window_location_),
+          gfx::PointF(target_window_root_location_),
+          target_current_context_->GetDragOperation());
       if (target_current_context_->source_client()) {
-        event.set_flags(target_current_context_->source_client()
-                            ->current_modifier_state());
+        drop_event.set_flags(
+            target_current_context_->source_client()->current_modifier_state());
       } else {
-        event.set_flags(XGetModifiers());
+        drop_event.set_flags(XGetModifiers());
       }
 
       if (!IsDragDropInProgress()) {
         UMA_HISTOGRAM_COUNTS_1M("Event.DragDrop.ExternalOriginDrop", 1);
       }
 
-      drag_operation = delegate->OnPerformDrop(event, std::move(data));
+      drag_operation = delegate->OnPerformDrop(drop_event, std::move(data));
     }
 
     target_window_->RemoveObserver(this);
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 617aa51..5a60f89e 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -608,7 +608,7 @@
     ui::WindowShowState* show_state) const {
   WINDOWPLACEMENT wp;
   wp.length = sizeof(wp);
-  const bool succeeded = !!::GetWindowPlacement(hwnd(), &wp);
+  bool succeeded = !!::GetWindowPlacement(hwnd(), &wp);
   DCHECK(succeeded);
 
   if (bounds != nullptr) {
@@ -616,15 +616,16 @@
       // GetWindowPlacement can return misleading position if a normalized
       // window was resized using Aero Snap feature (see comment 9 in bug
       // 36421). As a workaround, using GetWindowRect for normalized windows.
-      const bool succeeded = GetWindowRect(hwnd(), &wp.rcNormalPosition) != 0;
+      succeeded = GetWindowRect(hwnd(), &wp.rcNormalPosition) != 0;
       DCHECK(succeeded);
 
       *bounds = gfx::Rect(wp.rcNormalPosition);
     } else {
       MONITORINFO mi;
       mi.cbSize = sizeof(mi);
-      const bool succeeded = GetMonitorInfo(
-          MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST), &mi) != 0;
+      succeeded =
+          GetMonitorInfo(MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST),
+                         &mi) != 0;
       DCHECK(succeeded);
 
       *bounds = gfx::Rect(wp.rcNormalPosition);